1use cudarc::driver::sys::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES;
4use cudarc::driver::{
5 CudaContext, CudaFunction, CudaModule, CudaSlice, CudaStream, DeviceSlice, LaunchConfig,
6 PushKernelArg,
7};
8use cudarc::nvrtc::Ptx;
9use std::sync::{Arc, Mutex};
10
11const GDN_K2_DYNAMIC_SHARED_BYTES: u32 = 67_072;
12
13const SDPA_NAIVE_SMEM_MAX: usize = 48 * 1024;
18
19const SDPA_NAIVE_GMEM_WS_MAX: usize = 1 << 30;
23
24#[cfg(debug_assertions)]
25pub(crate) fn debug_assert_tensor_stream_device<T>(
26 tensor: &CudaSlice<T>,
27 stream: &CudaStream,
28 site: &str,
29) {
30 let tensor_dev = tensor.ordinal();
31 let stream_dev = stream.context().ordinal();
32 assert_eq!(
33 tensor_dev, stream_dev,
34 "PP cross-device tensor read at {site}: tensor on dev{tensor_dev}, stream on dev{stream_dev}"
35 );
36}
37
38fn ensure_tensor_stream_device<T>(
39 tensor: &impl DeviceSlice<T>,
40 stream: &CudaStream,
41 site: &str,
42) -> Result<(), Box<dyn std::error::Error>> {
43 let tensor_dev = tensor.stream().context().ordinal();
44 let stream_dev = stream.context().ordinal();
45 if tensor_dev != stream_dev {
46 return Err(format!(
47 "PP cross-device tensor access at {site}: tensor on dev{tensor_dev}, \
48 stream on dev{stream_dev}"
49 )
50 .into());
51 }
52 Ok(())
53}
54
55pub use memra_gguf;
56pub use memra_runtime;
57
58pub mod forward;
59pub mod hybrid;
60pub mod hybrid_forward;
61pub mod model;
62pub mod sigrouter_contract;
63pub mod vision;
64pub mod vision_gemma;
65pub mod vision_pre;
66pub mod cache {
69 pub use memra_kv::*;
70}
71pub mod decode;
72pub mod decode_batch;
73pub mod dflash;
74pub mod eagle;
75pub mod gemma_spec;
76pub mod graph_update;
77pub mod mla;
81pub mod moesd;
82pub mod parallel;
83pub mod plan_backend;
84pub mod pp;
85pub mod round_stream;
86pub mod spec;
87pub mod tp;
88pub use memra_sampling as sampler;
89
90pub fn moe_f16g_mode() -> u8 {
134 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
135 *M.get_or_init(|| match std::env::var("MEMRA_MOE_F16G").as_deref() {
136 Ok("0") => 0,
137 Ok("2") => 2,
138 Ok("3") => 3,
139 Ok(_) => 1,
140 Err(_) => 2,
143 })
144}
145pub fn moe_f16g_sk_params() -> (i32, i32) {
159 static P: std::sync::OnceLock<(i32, i32)> = std::sync::OnceLock::new();
160 *P.get_or_init(|| match std::env::var("MEMRA_F16G_SK").as_deref() {
161 Ok("0") => (-1, 0),
162 Ok("32") => (0, i32::MAX),
163 Ok("128") => (0, 1),
164 _ => {
165 let cross = std::env::var("MEMRA_F16G_SK_CROSS")
166 .ok()
167 .and_then(|v| v.parse().ok())
168 .unwrap_or(64);
169 (0, cross)
170 }
171 })
172}
173pub fn moe_f16g_direct_on(qtype: i32) -> bool {
184 static M: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
185 let m = *M.get_or_init(|| match std::env::var("MEMRA_F16G_DIRECT").as_deref() {
186 Ok("0") => 0,
187 Ok("kq") => 1,
188 _ => 2,
189 });
190 match m {
191 0 => false,
192 1 => qtype == QT_Q4_K || qtype == QT_Q6_K,
193 _ => true,
194 }
195}
196pub fn moe_f16g_tail_on() -> bool {
205 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
206 *ON.get_or_init(|| std::env::var("MEMRA_F16G_TAIL").as_deref() != Ok("0"))
207}
208
209pub fn moe_f16g_gemma_on() -> bool {
216 static M: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
217 *M.get_or_init(|| !matches!(std::env::var("MEMRA_MOE_F16G").as_deref(), Ok("0") | Err(_)))
218}
219
220pub fn moe_fuse_actq_on() -> bool {
224 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
225 *ON.get_or_init(|| std::env::var("MEMRA_MOE_FUSE_ACTQ").as_deref() != Ok("0"))
226}
227
228pub fn router_prefill_exact_on() -> bool {
238 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
239 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_PREFILL_EXACT").as_deref() != Ok("0"))
240}
241
242pub fn router_kernel_on() -> bool {
243 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
244 *ON.get_or_init(|| {
245 let on = std::env::var("MEMRA_ROUTER_KERNEL").as_deref() != Ok("0");
246 if !on {
247 eprintln!("[memra] router kernel OFF (rollback: per-column cuBLAS gemv)");
248 }
249 on
250 })
251}
252
253pub const ROUTER_BATCH_MIN_T: usize = 8;
268pub fn router_batch_on() -> bool {
269 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
270 *ON.get_or_init(|| std::env::var("MEMRA_ROUTER_BATCH").as_deref() != Ok("0"))
271}
272mod cpu_experts;
273#[cfg(memra_cutlass)]
274pub mod cutlass_ffi;
275pub mod dsv4_ffi;
276pub mod dsv4_gpu;
277pub mod f16_ffi;
278pub mod fp8_ffi;
279pub mod mmq_ffi;
280pub mod moe_cache;
281pub mod prime_graph;
282pub mod spill;
283mod spill_pread;
284
285const FATBIN: &[u8] = include_bytes!(env!("MEMRA_ENGINE_FATBIN"));
292const HYBRID_FATBIN: &[u8] = include_bytes!(env!("MEMRA_HYBRID_FATBIN"));
293const QMATVEC_FATBIN: &[u8] = include_bytes!(env!("MEMRA_QMATVEC_FATBIN"));
294const FLASH_FATBIN: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN"));
295const GEMM_FATBIN: &[u8] = include_bytes!(env!("MEMRA_GEMM_FATBIN"));
296const ROUTER_FATBIN: &[u8] = include_bytes!(env!("MEMRA_ROUTER_FATBIN"));
297const SAMPLE_FATBIN: &[u8] = include_bytes!(env!("MEMRA_SAMPLE_FATBIN"));
299
300fn gemm_fatbin_bytes() -> std::borrow::Cow<'static, [u8]> {
306 assert!(
307 !(portable_mma_gated() && std::env::var_os("MEMRA_GEMM_FATBIN").is_some()),
308 "MEMRA_GEMM_FATBIN overrides are not allowed in the portable CUDA lane"
309 );
310 match std::env::var("MEMRA_GEMM_FATBIN") {
311 Ok(path) => std::borrow::Cow::Owned(
312 std::fs::read(&path).unwrap_or_else(|e| panic!("MEMRA_GEMM_FATBIN read {path}: {e}")),
313 ),
314 Err(_) => std::borrow::Cow::Borrowed(GEMM_FATBIN),
315 }
316}
317
318pub(crate) const fn portable_mma_gated() -> bool {
325 cfg!(memra_portable_cuda) && !cfg!(memra_hopper_mma)
326}
327
328#[track_caller]
345pub(crate) fn refuse_portable_force(var: &str, needs: &str) {
346 assert!(
347 !portable_mma_gated(),
348 "{var} forces a kernel path this build does not contain: it needs {needs}, and this is a \
349 portable-CUDA build (sm_89). Unset {var} — the default path serves this arch."
350 );
351}
352
353pub(crate) const fn gdn_mma_default_on() -> bool {
362 cfg!(memra_hopper_mma) || konst_eq(env!("MEMRA_BUILT_CUDA_ARCH"), "120a")
363}
364
365const fn konst_eq(a: &str, b: &str) -> bool {
367 let (a, b) = (a.as_bytes(), b.as_bytes());
368 if a.len() != b.len() {
369 return false;
370 }
371 let mut i = 0;
372 while i < a.len() {
373 if a[i] != b[i] {
374 return false;
375 }
376 i += 1;
377 }
378 true
379}
380
381const fn legacy_quant_gemm_allowed(portable_cuda: bool, hopper_mma: bool, no_gemm: bool) -> bool {
386 (!portable_cuda || hopper_mma) && !no_gemm
387}
388
389const FLASH_FATBIN_VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VQ4"));
397const FLASH_FATBIN_VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_VF8"));
398const FLASH_FATBIN_KF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8"));
399const FLASH_FATBIN_KF8VQ4: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VQ4"));
400const FLASH_FATBIN_KF8VF8: &[u8] = include_bytes!(env!("MEMRA_FLASH_FATBIN_KF8VF8"));
401
402pub use memra_kv::{kv_blk_bytes, kv_cache_formats};
405
406fn flash_fatbin_bytes() -> &'static [u8] {
408 match kv_cache_formats() {
409 ("q8_0", "q5_1") => FLASH_FATBIN,
410 ("q8_0", "q4_0") => FLASH_FATBIN_VQ4,
411 ("q8_0", "fp8") => FLASH_FATBIN_VF8,
412 ("fp8", "q5_1") => FLASH_FATBIN_KF8,
413 ("fp8", "q4_0") => FLASH_FATBIN_KF8VQ4,
414 ("fp8", "fp8") => FLASH_FATBIN_KF8VF8,
415 other => unreachable!("kv_cache_formats returned {other:?}"),
416 }
417}
418
419fn k1_launch_override() -> Option<(u32, u32, u32)> {
426 static K1: std::sync::OnceLock<Option<(u32, u32, u32)>> = std::sync::OnceLock::new();
427 *K1.get_or_init(|| {
428 let v = std::env::var("MEMRA_GEMM_K1_LAUNCH").ok()?;
429 let p: Vec<u32> = v.split(',').filter_map(|s| s.trim().parse().ok()).collect();
430 match p.as_slice() {
431 [bm, bn, w] => Some((*bm, *bn, *w)),
432 _ => None,
433 }
434 })
435}
436
437pub(crate) fn wgmma_gemm_enabled() -> bool {
444 static V: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
445 *V.get_or_init(|| std::env::var("MEMRA_WGMMA").as_deref() == Ok("1"))
446}
447
448pub const FA_VEC_MIN_TKV: usize = 96;
463pub fn fa_vec_min_tkv() -> usize {
467 static V: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
468 *V.get_or_init(|| {
469 std::env::var("MEMRA_FA_VEC_MIN")
470 .ok()
471 .and_then(|v| v.parse().ok())
472 .unwrap_or_else(|| FA_VEC_MIN_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
473 })
474}
475
476pub fn fa_f16pv_on() -> bool {
487 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
488 *ON.get_or_init(|| {
489 std::env::var("MEMRA_FA_F16PV")
490 .map(|v| v != "0")
491 .unwrap_or_else(|_| std::env::var("MEMRA_DRAFT").is_err())
492 })
493}
494
495pub fn fa512_hp_on() -> bool {
499 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
500 *ON.get_or_init(|| std::env::var("MEMRA_FA512_HP").as_deref() != Ok("0"))
501}
502
503pub fn faw_hp_on() -> bool {
507 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
508 *ON.get_or_init(|| std::env::var("MEMRA_FAW_HP").as_deref() != Ok("0"))
509}
510
511pub fn fa512_wide_warps() -> usize {
515 static N: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
516 *N.get_or_init(|| match std::env::var("MEMRA_FA512_W4").as_deref() {
517 Ok("1") => 4,
518 _ => 2,
519 })
520}
521
522pub fn fa512_min_tkv() -> usize {
525 static FA512_MIN: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
526 *FA512_MIN.get_or_init(|| {
527 std::env::var("MEMRA_FA512_MIN")
528 .ok()
529 .and_then(|v| v.parse().ok())
530 .unwrap_or(512)
531 })
532}
533pub static FA_VEC_MIN_DEFAULT: std::sync::atomic::AtomicUsize =
537 std::sync::atomic::AtomicUsize::new(FA_VEC_MIN_TKV);
538pub static FA_SPW_DEFAULT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(32);
542pub static FUSED_MR1_DEFAULT: std::sync::atomic::AtomicBool =
548 std::sync::atomic::AtomicBool::new(false);
549pub static ROUTER_W8_DEFAULT: std::sync::atomic::AtomicBool =
556 std::sync::atomic::AtomicBool::new(true);
557pub static FA_SP512_DEFAULT: std::sync::atomic::AtomicUsize =
558 std::sync::atomic::AtomicUsize::new(16);
559pub static RMS_BLOCK_DEFAULT: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(256);
564pub static FA_SP_GEMMA: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
566pub static MMQ_SK_FORCE: std::sync::atomic::AtomicI8 = std::sync::atomic::AtomicI8::new(-1);
571pub use memra_kv::KV_FP8_FORCE;
574pub(crate) fn mmv_block() -> u32 {
579 static V: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
580 *V.get_or_init(|| {
581 std::env::var("MEMRA_MMV_BLOCK")
582 .ok()
583 .and_then(|v| v.parse().ok())
584 .filter(|&b: &u32| (64..=256).contains(&b) && b % 32 == 0)
585 .unwrap_or(128)
586 })
587}
588
589pub(crate) fn sig_expf_dev_on() -> bool {
594 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
595 *ON.get_or_init(|| std::env::var("MEMRA_SIG_EXPF_DEV").as_deref() == Ok("1"))
596}
597
598pub(crate) fn topk_fast_on() -> bool {
599 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
600 *ON.get_or_init(|| std::env::var("MEMRA_TOPK_FAST").as_deref() == Ok("1"))
601}
602
603pub(crate) fn rms_block() -> u32 {
604 static V: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
605 *V.get_or_init(|| {
606 std::env::var("MEMRA_RMS_BLOCK")
607 .ok()
608 .and_then(|v| v.parse().ok())
609 .unwrap_or_else(|| RMS_BLOCK_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
610 })
611}
612
613pub(crate) fn fa_split_keys(t_kv: usize, n_head_kv: usize) -> usize {
614 static S: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
615 if let Some(forced) = *S.get_or_init(|| {
616 std::env::var("MEMRA_FA_SPLIT")
617 .ok()
618 .and_then(|v| v.parse().ok())
619 .filter(|&s: &usize| s >= 8 && s % 8 == 0)
620 }) {
621 return forced;
622 }
623 if FA_SP_GEMMA.load(std::sync::atomic::Ordering::Relaxed)
641 && std::env::var("MEMRA_FA_SP16").as_deref() == Ok("1")
642 {
643 return if t_kv <= 8192 {
644 16
645 } else if t_kv <= 16384 {
646 64
647 } else {
648 128
649 };
650 }
651 let big_rig = fa_sm_count() >= 128;
652 if big_rig {
653 let _ = n_head_kv;
654 if t_kv <= 2048 {
655 static SHORT: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
664 if let Some(sp) = *SHORT.get_or_init(|| {
665 std::env::var("MEMRA_FA_SP_SHORT")
666 .ok()
667 .and_then(|v| v.parse().ok())
668 .filter(|&s: &usize| s >= 8 && s % 8 == 0)
669 }) {
670 return sp;
671 }
672 16
673 } else if t_kv <= 16384 {
674 64
675 } else {
676 128
677 }
678 } else if n_head_kv <= 4 {
679 if t_kv <= 512 {
700 8
701 } else if t_kv <= 16384 {
702 64
703 } else {
704 128
705 }
706 } else {
707 if t_kv <= 8192 {
708 32
709 } else if t_kv <= 16384 {
710 64
711 } else {
712 128
713 }
714 }
715}
716
717fn fa_sm_count() -> i32 {
720 static N: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
721 *N.get_or_init(|| {
722 cudarc::driver::result::init().ok();
723 cudarc::driver::result::device::get(0)
724 .and_then(|d| unsafe { cudarc::driver::result::device::get_attribute(
725 d, cudarc::driver::sys::CUdevice_attribute_enum::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT) })
726 .unwrap_or(82)
727 })
728}
729
730fn fa_hd_suffix(head_dim: usize) -> Result<&'static str, Box<dyn std::error::Error>> {
734 match head_dim {
735 256 => Ok(""),
736 128 => Ok("_hd128"),
737 d => Err(format!(
738 "fa_prefill: no kernel stamped for head_dim={d} (only 256/128); \
739 callers must gate to sdpa_naive"
740 )
741 .into()),
742 }
743}
744
745pub const QT_Q8_0: i32 = 0;
747pub const QT_Q4_K: i32 = 1;
748pub const QT_Q6_K: i32 = 2;
749pub const QT_Q5_K: i32 = 3;
750pub const QT_Q3_K: i32 = 4;
751pub const QT_IQ4_XS: i32 = 5;
752pub const QT_IQ3_S: i32 = 6;
753pub const QT_NVFP4: i32 = 7;
754pub const QT_F8_E4M3: i32 = 10;
760pub const QT_NVFP4_RP: i32 = 9;
763pub const QT_F32: i32 = 8;
765pub const QT_BF16: i32 = 11;
766pub const QT_Q4_0: i32 = 12; pub const QT_Q2_K: i32 = 13;
771pub const QT_F8_E4M3_BLK: i32 = 14;
787
788pub struct Engine {
790 pub gpu: memra_runtime::Gpu,
791 module: Arc<CudaModule>,
792 hybrid: Arc<CudaModule>,
793 qmatvec: Arc<CudaModule>,
794 flash: Arc<CudaModule>,
795 flash_g: std::sync::OnceLock<Arc<CudaModule>>,
799 gemm: Arc<CudaModule>,
800 router: Arc<CudaModule>,
801 sample: Arc<CudaModule>,
803 moe_cache: Mutex<Option<crate::moe_cache::MoeSlotCache>>,
807 moe_cache_layout: Mutex<Option<Vec<usize>>>,
811 capture_keep_on: std::sync::atomic::AtomicBool,
817 verify_exact: std::sync::atomic::AtomicBool,
822 capture_keep: Mutex<Vec<Box<dyn std::any::Any + Send>>>,
823 pub copy_stream: Arc<CudaStream>,
825 #[cfg(memra_cutlass)]
832 cutlass_scratch: Mutex<Option<crate::cutlass_ffi::CutlassScratch>>,
833 fp8_scratch: Mutex<Option<crate::fp8_ffi::Fp8Scratch>>,
837 fa_vf16_scratch: Mutex<Option<CudaSlice<u8>>>,
840 fa_part_pool: Mutex<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
844 fa_part_retired: Mutex<Vec<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>>,
848 fn_cache: Mutex<std::collections::HashMap<String, CudaFunction>>,
850 f16_scratch: Mutex<Option<crate::f16_ffi::F16Scratch>>,
851 argmax_partials: Mutex<Option<(CudaSlice<f32>, CudaSlice<i32>)>>,
856 prime_deqw_ws: Mutex<Option<(CudaSlice<u8>, CudaSlice<u8>)>>,
861 router_stage: Mutex<Option<PinnedStage>>,
865}
866
867fn fa_v2_on() -> bool {
877 std::env::var("MEMRA_FA_V2")
883 .map(|v| v != "0")
884 .unwrap_or(true)
885}
886
887pub(crate) fn fa_v3_on() -> bool {
895 std::env::var("MEMRA_FA_V3")
899 .map(|v| v != "0")
900 .unwrap_or(true)
901}
902
903fn fa_v4_mode() -> &'static str {
908 static M: std::sync::OnceLock<String> = std::sync::OnceLock::new();
909 M.get_or_init(|| std::env::var("MEMRA_FA_V4").unwrap_or_default())
910}
911fn fa_v4_on() -> bool {
912 fa_v4_mode() != "0"
913} pub static FA_SMEM_TKV_DEFAULT: std::sync::atomic::AtomicUsize =
922 std::sync::atomic::AtomicUsize::new(1024);
923pub static FA_V4_MAX_DEFAULT: std::sync::atomic::AtomicUsize =
924 std::sync::atomic::AtomicUsize::new(usize::MAX);
925pub fn fa_v4_at_pub(t_kv: usize) -> bool {
926 fa_v4_at(t_kv)
927}
928fn fa_v4_at(t_kv: usize) -> bool {
929 static M: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
930 let mx = *M.get_or_init(|| {
931 std::env::var("MEMRA_FA_V4_MAX")
932 .ok()
933 .and_then(|v| v.parse().ok())
934 .unwrap_or_else(|| FA_V4_MAX_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
935 });
936 fa_v4_on() && t_kv < mx
937}
938pub const FA_DEEP_MIN_DEFAULT: usize = 0;
952fn fa_deep_at(t_kv: usize) -> bool {
953 if std::env::var("MEMRA_FA_DEEP").as_deref() == Ok("0") {
954 return false;
955 }
956 let min = std::env::var("MEMRA_FA_DEEP_MIN")
957 .ok()
958 .and_then(|v| v.parse().ok())
959 .unwrap_or(FA_DEEP_MIN_DEFAULT);
960 t_kv >= min
961}
962pub fn fa_deep_at_pub(t_kv: usize) -> bool {
964 fa_deep_at(t_kv)
965}
966
967fn fa_v3_active(head_dim: usize) -> bool {
968 fa_v3_on()
971 && head_dim % 128 == 0
972 && kv_cache_formats() == ("q8_0", "q5_1")
973 && !Engine::kv_fp8_on()
974}
975
976pub fn fa_seqs_eligible(t_kv: usize, head_dim: usize) -> bool {
984 std::env::var("MEMRA_NO_FA_VEC").is_err()
985 && t_kv >= fa_vec_min_tkv()
986 && head_dim == 256
987 && fa_v4_at(t_kv)
988 && !matches!(fa_v4_mode(), "noB3" | "stage")
989 && !Engine::kv_fp8_on()
990}
991pub fn fa_split_keys_pub(t_kv: usize, n_head_kv: usize) -> usize {
993 fa_split_keys(t_kv, n_head_kv)
994}
995
996struct PinnedStage {
1001 ptr: *mut u8,
1002 cap: usize,
1003}
1004unsafe impl Send for PinnedStage {}
1005impl PinnedStage {
1006 fn new(cap: usize) -> Result<Self, Box<dyn std::error::Error>> {
1007 let ptr = unsafe { cudarc::driver::result::malloc_host(cap, 0)? } as *mut u8;
1008 Ok(PinnedStage { ptr, cap })
1009 }
1010}
1011impl Drop for PinnedStage {
1012 fn drop(&mut self) {
1013 let _ = unsafe { cudarc::driver::result::free_host(self.ptr as _) };
1014 }
1015}
1016
1017pub const ARGMAX_NB: usize = 256;
1020
1021pub(crate) use memra_fa3_vl as fa3_vl_raw;
1023
1024unsafe extern "C" {
1025 fn memra_fa3_prefill(
1027 q16: *const core::ffi::c_void,
1028 k16: *const core::ffi::c_void,
1029 v16: *const core::ffi::c_void,
1030 o: *mut f32,
1031 t: i32,
1032 h: i32,
1033 hkv: i32,
1034 d: i32,
1035 scale: f32,
1036 stream: *mut core::ffi::c_void,
1037 ) -> i32;
1038 pub(crate) fn memra_fa3_vl(
1040 q16s: *const *const core::ffi::c_void,
1041 k16s: *const *const core::ffi::c_void,
1042 v16s: *const *const core::ffi::c_void,
1043 os: *const *mut f32,
1044 ts: *const i32,
1045 b: i32,
1046 h: i32,
1047 hkv: i32,
1048 d: i32,
1049 scale: f32,
1050 stream: *mut core::ffi::c_void,
1051 ) -> i32;
1052}
1053
1054#[repr(C)]
1059#[derive(Clone, Copy)]
1060pub struct WPtr8(pub [u64; 8]);
1061unsafe impl cudarc::driver::DeviceRepr for WPtr8 {}
1062
1063#[repr(C)]
1068#[derive(Clone, Copy, Default)]
1069pub struct GdnSeqVl {
1070 pub kb16: u64,
1071 pub gcum: u64,
1072 pub beta: u64,
1073 pub u: u64,
1074 pub wb16: u64,
1075 pub y: u64,
1076 pub ssnap: u64,
1077 pub state_in: u64,
1078 pub state_out: u64,
1079 pub q: u64,
1080 pub p: u64,
1081 pub o: u64,
1082 pub k: u64,
1083 pub v: u64,
1084 pub g: u64,
1085 pub a: u64,
1086 pub w: u64,
1087 pub t: i32,
1088 pub nc: i32,
1089}
1090unsafe impl cudarc::driver::DeviceRepr for GdnSeqVl {}
1091#[repr(C)]
1092#[derive(Clone, Copy)]
1093pub struct GdnVl8(pub [GdnSeqVl; 8]);
1094unsafe impl cudarc::driver::DeviceRepr for GdnVl8 {}
1095
1096#[repr(C)]
1099#[derive(Clone, Copy, Default)]
1100pub struct GdnWVl {
1101 pub qb16: u64,
1102 pub pb16: u64,
1103}
1104unsafe impl cudarc::driver::DeviceRepr for GdnWVl {}
1105#[repr(C)]
1106#[derive(Clone, Copy)]
1107pub struct GdnWVl8(pub [GdnWVl; 8]);
1108unsafe impl cudarc::driver::DeviceRepr for GdnWVl8 {}
1109
1110#[repr(C)]
1112#[derive(Clone, Copy, Default)]
1113pub struct GdnPrepVl {
1114 pub qkv: u64,
1115 pub conv_state: u64,
1116 pub conv_out: u64,
1117 pub q_g: u64,
1118 pub k_g: u64,
1119 pub v_g: u64,
1120 pub q_l2: u64,
1121 pub k_l2: u64,
1122 pub beta_raw: u64,
1123 pub alpha: u64,
1124 pub beta: u64,
1125 pub g_log: u64,
1126 pub o: u64,
1127 pub z: u64,
1128 pub gn: u64,
1129 pub gn16: u64,
1130 pub kb16: u64,
1131 pub qb16: u64,
1132 pub t: i32,
1133 pub pad: i32,
1134}
1135unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl {}
1136#[repr(C)]
1137#[derive(Clone, Copy)]
1138pub struct GdnPrepVl8(pub [GdnPrepVl; 8]);
1139unsafe impl cudarc::driver::DeviceRepr for GdnPrepVl8 {}
1140
1141#[repr(C)]
1143#[derive(Clone, Copy, Default)]
1144pub struct FaSeqVl {
1145 pub q: u64,
1146 pub k16: u64,
1147 pub v16: u64,
1148 pub o: u64,
1149 pub kf: u64,
1150 pub vf: u64,
1151 pub t: i32,
1152 pub pad: i32,
1153}
1154unsafe impl cudarc::driver::DeviceRepr for FaSeqVl {}
1155#[repr(C)]
1156#[derive(Clone, Copy)]
1157pub struct FaVl8(pub [FaSeqVl; 8]);
1158unsafe impl cudarc::driver::DeviceRepr for FaVl8 {}
1159
1160#[repr(C)]
1162#[derive(Clone, Copy, Default)]
1163pub struct AttnPreVl {
1164 pub qf: u64,
1165 pub kf: u64,
1166 pub vf: u64,
1167 pub q: u64,
1168 pub gate: u64,
1169 pub qn: u64,
1170 pub kn: u64,
1171 pub kc: u64,
1172 pub vc: u64,
1173 pub t: i32,
1174 pub pad: i32,
1175}
1176unsafe impl cudarc::driver::DeviceRepr for AttnPreVl {}
1177#[repr(C)]
1178#[derive(Clone, Copy)]
1179pub struct AttnPreVl8(pub [AttnPreVl; 8]);
1180unsafe impl cudarc::driver::DeviceRepr for AttnPreVl8 {}
1181
1182pub struct GdnChunkBufs {
1185 pub gcum: CudaSlice<f32>,
1186 pub a: CudaSlice<f32>,
1187 pub p: CudaSlice<f32>,
1188 pub u: CudaSlice<f32>,
1189 pub w: CudaSlice<f32>,
1190 pub kb16: CudaSlice<u8>,
1191 pub wb16: CudaSlice<u8>,
1192 pub y16: CudaSlice<u8>,
1193 pub ssnap16: CudaSlice<u8>,
1194 pub qb16: CudaSlice<u8>,
1195 pub pb16: CudaSlice<u8>,
1196 pub o: CudaSlice<f32>,
1197 pub t: usize,
1198 pub nc: usize,
1199}
1200
1201#[repr(C)]
1203#[derive(Clone, Copy)]
1204pub struct F32x8(pub [f32; 8]);
1205unsafe impl cudarc::driver::DeviceRepr for F32x8 {}
1206
1207pub static PRIME_NANOS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
1211
1212impl Engine {
1213 pub fn new(ordinal: usize) -> Result<Self, Box<dyn std::error::Error>> {
1214 let gpu = memra_runtime::Gpu::new(ordinal)?;
1215 if std::env::var("MEMRA_ARCH_CHECK").as_deref() != Ok("0") {
1219 use cudarc::driver::sys::CUdevice_attribute_enum as A;
1220 let (maj, min) = cudarc::driver::result::device::get(ordinal as i32)
1221 .and_then(|d| unsafe {
1222 Ok((
1223 cudarc::driver::result::device::get_attribute(
1224 d,
1225 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR,
1226 )?,
1227 cudarc::driver::result::device::get_attribute(
1228 d,
1229 A::CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR,
1230 )?,
1231 ))
1232 })
1233 .unwrap_or((0, 0));
1234 let built = env!("MEMRA_BUILT_CUDA_ARCH");
1235 let ok = matches!(
1236 (built, maj, min),
1237 ("120a", 12, 0) | ("120a", 12, 1) | ("100a", 10, 0) | ("90a", 9, 0) | ("89", 8, 9)
1238 );
1239 if !ok {
1240 return Err(format!(
1241 "memra was built for sm_{built} but device {ordinal} reports compute \
1242 capability {maj}.{min}. Rebuild on this machine (MEMRA_CUDA_ARCH \
1243 auto-detects the GPU) or set MEMRA_ARCH_CHECK=0 to bypass."
1244 )
1245 .into());
1246 }
1247 }
1248 unsafe {
1253 use cudarc::driver::sys;
1254 let dev: sys::CUdevice = ordinal as sys::CUdevice;
1255 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1256 if sys::cuDeviceGetDefaultMemPool(&mut pool, dev) == sys::CUresult::CUDA_SUCCESS {
1257 let mut thresh: u64 = u64::MAX;
1258 let _ = sys::cuMemPoolSetAttribute(
1259 pool,
1260 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RELEASE_THRESHOLD,
1261 &mut thresh as *mut u64 as *mut core::ffi::c_void,
1262 );
1263 }
1264 }
1265 let module = gpu.ctx.load_module(Ptx::from_binary(FATBIN.to_vec()))?;
1266 let hybrid = gpu
1267 .ctx
1268 .load_module(Ptx::from_binary(HYBRID_FATBIN.to_vec()))?;
1269 let qmatvec = gpu
1270 .ctx
1271 .load_module(Ptx::from_binary(QMATVEC_FATBIN.to_vec()))?;
1272 let flash = gpu
1273 .ctx
1274 .load_module(Ptx::from_binary(flash_fatbin_bytes().to_vec()))?;
1275 let gemm = gpu
1276 .ctx
1277 .load_module(Ptx::from_binary(gemm_fatbin_bytes().into_owned()))?;
1278 let router = gpu
1279 .ctx
1280 .load_module(Ptx::from_binary(ROUTER_FATBIN.to_vec()))?;
1281 let sample = gpu
1282 .ctx
1283 .load_module(Ptx::from_binary(SAMPLE_FATBIN.to_vec()))?;
1284 let copy_stream = gpu.ctx.new_stream()?;
1285 if std::env::var("MEMRA_EVT")
1301 .map(|v| v == "1")
1302 .unwrap_or(false)
1303 {
1304 } else {
1306 unsafe {
1307 gpu.ctx.disable_event_tracking();
1308 }
1309 }
1310 Ok(Self {
1311 gpu,
1312 module,
1313 hybrid,
1314 qmatvec,
1315 flash,
1316 flash_g: std::sync::OnceLock::new(),
1317 gemm,
1318 router,
1319 sample,
1320 moe_cache: Mutex::new(None),
1321 moe_cache_layout: Mutex::new(None),
1322 copy_stream,
1323 capture_keep_on: std::sync::atomic::AtomicBool::new(false),
1324 verify_exact: std::sync::atomic::AtomicBool::new(false),
1325 capture_keep: Mutex::new(Vec::new()),
1326 argmax_partials: Mutex::new(None),
1327 prime_deqw_ws: Mutex::new(None),
1328 router_stage: Mutex::new(None),
1329 fp8_scratch: Mutex::new(None),
1330 fa_vf16_scratch: Mutex::new(None),
1331 fa_part_pool: Mutex::new(None),
1332 fa_part_retired: Mutex::new(Vec::new()),
1333 fn_cache: Mutex::new(Default::default()),
1334 f16_scratch: Mutex::new(None),
1335 #[cfg(memra_cutlass)]
1336 cutlass_scratch: Mutex::new(None),
1337 })
1338 }
1339
1340 pub fn ctx(&self) -> &Arc<CudaContext> {
1341 &self.gpu.ctx
1342 }
1343
1344 pub fn pool_cached_bytes(&self) -> usize {
1362 let (reserved, used) = self.pool_reserved_used();
1363 reserved.saturating_sub(used)
1364 }
1365
1366 pub fn device_graph_mem_reserved(&self) -> usize {
1376 use cudarc::driver::sys as cus;
1377 let Ok(dev) = cudarc::driver::result::device::get(self.gpu.ctx.ordinal() as i32) else {
1378 return 0;
1379 };
1380 let mut bytes: u64 = 0;
1381 let rc = unsafe {
1382 cus::cuDeviceGetGraphMemAttribute(
1383 dev,
1384 cus::CUgraphMem_attribute::CU_GRAPH_MEM_ATTR_RESERVED_MEM_CURRENT,
1385 &mut bytes as *mut u64 as *mut std::ffi::c_void,
1386 )
1387 };
1388 if rc == cus::cudaError_enum::CUDA_SUCCESS {
1389 bytes as usize
1390 } else {
1391 0
1392 }
1393 }
1394
1395 pub fn pool_reserved_used(&self) -> (usize, usize) {
1402 use cudarc::driver::sys;
1403 unsafe {
1404 let mut pool: sys::CUmemoryPool = std::ptr::null_mut();
1405 if sys::cuDeviceGetDefaultMemPool(&mut pool, self.gpu.ctx.ordinal() as sys::CUdevice)
1406 != sys::CUresult::CUDA_SUCCESS
1407 {
1408 return (0, 0);
1409 }
1410 let (mut reserved, mut used) = (0u64, 0u64);
1411 if sys::cuMemPoolGetAttribute(
1412 pool,
1413 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_RESERVED_MEM_CURRENT,
1414 &mut reserved as *mut u64 as *mut core::ffi::c_void,
1415 ) != sys::CUresult::CUDA_SUCCESS
1416 {
1417 return (0, 0);
1418 }
1419 if sys::cuMemPoolGetAttribute(
1420 pool,
1421 sys::CUmemPool_attribute_enum::CU_MEMPOOL_ATTR_USED_MEM_CURRENT,
1422 &mut used as *mut u64 as *mut core::ffi::c_void,
1423 ) != sys::CUresult::CUDA_SUCCESS
1424 {
1425 return (0, 0);
1426 }
1427 (reserved as usize, used as usize)
1428 }
1429 }
1430
1431 pub fn stream(&self) -> Arc<CudaStream> {
1434 self.gpu.stream()
1435 }
1436 pub fn gkv_on() -> bool {
1439 memra_kv::gkv_on()
1440 }
1441
1442 pub fn wkv_on() -> bool {
1454 memra_kv::wkv_on()
1455 }
1456
1457 pub fn kv_fp8_on() -> bool {
1463 memra_kv::kv_fp8_on()
1464 }
1465
1466 fn fa_func(&self, name: &str, head_dim: usize) -> CudaFunction {
1469 if head_dim == 512 && Self::gkv_on() {
1470 self.func_g(name)
1471 } else {
1472 self.func(name)
1473 }
1474 }
1475
1476 fn func_g(&self, name: &str) -> CudaFunction {
1480 let m = self.flash_g.get_or_init(|| {
1481 self.gpu
1482 .ctx
1483 .load_module(cudarc::nvrtc::Ptx::from_binary(
1484 FLASH_FATBIN_KF8VF8.to_vec(),
1485 ))
1486 .expect("load kf8vf8 flash fatbin (fp8-globals arm)")
1487 });
1488 let key = format!("g:{name}");
1489 if let Some(f) = self.fn_cache.lock().unwrap().get(&key) {
1490 return f.clone();
1491 }
1492 let f = match m.load_function(name) {
1493 Ok(f) => f,
1494 Err(_) => self.func(name),
1495 };
1496 self.fn_cache.lock().unwrap().insert(key, f.clone());
1497 f
1498 }
1499
1500 fn func(&self, name: &str) -> CudaFunction {
1501 if let Some(f) = self.fn_cache.lock().unwrap().get(name) {
1504 return f.clone();
1505 }
1506 let f = self
1507 .module
1508 .load_function(name)
1509 .or_else(|_| self.hybrid.load_function(name))
1510 .or_else(|_| self.qmatvec.load_function(name))
1511 .or_else(|_| self.flash.load_function(name))
1512 .or_else(|_| self.gemm.load_function(name))
1513 .or_else(|_| self.router.load_function(name))
1514 .or_else(|_| self.sample.load_function(name))
1515 .unwrap_or_else(|_| panic!("kernel {name} not in any fatbin"));
1516 self.fn_cache
1517 .lock()
1518 .unwrap()
1519 .insert(name.to_string(), f.clone());
1520 f
1521 }
1522
1523 pub fn scatter_trim_logits(
1526 &self,
1527 src: &CudaSlice<f32>,
1528 d2t: &CudaSlice<u32>,
1529 dst: &mut CudaSlice<f32>,
1530 d_vocab: usize,
1531 n_vocab: usize,
1532 ) -> Result<(), Box<dyn std::error::Error>> {
1533 let f1 = self.func("scatter_trim_logits_f32");
1534 let f2 = self.func("scatter_trim_logits_pass2_f32");
1535 let (dv, nv) = (d_vocab as i32, n_vocab as i32);
1536 let cfg1 = LaunchConfig {
1537 grid_dim: (256, 1, 1),
1538 block_dim: (256, 1, 1),
1539 shared_mem_bytes: 0,
1540 };
1541 let __s_b1 = self.gpu.stream();
1542 let mut b1 = __s_b1.launch_builder(&f1);
1543 b1.arg(src).arg(d2t).arg(&mut *dst).arg(&dv).arg(&nv);
1544 unsafe {
1545 b1.launch(cfg1)?;
1546 }
1547 let cfg2 = LaunchConfig {
1548 grid_dim: (d_vocab.div_ceil(256) as u32, 1, 1),
1549 block_dim: (256, 1, 1),
1550 shared_mem_bytes: 0,
1551 };
1552 let __s_b2 = self.gpu.stream();
1553 let mut b2 = __s_b2.launch_builder(&f2);
1554 b2.arg(src).arg(d2t).arg(&mut *dst).arg(&dv);
1555 unsafe {
1556 b2.launch(cfg2)?;
1557 }
1558 Ok(())
1559 }
1560
1561 #[allow(clippy::too_many_arguments)]
1567 pub fn filter_stats(
1568 &self,
1569 x: &CudaSlice<f32>,
1570 row_stride: usize,
1571 rows: &CudaSlice<i32>,
1572 out_th: &mut CudaSlice<f32>,
1573 out_z: &mut CudaSlice<f32>,
1574 out_max: &mut CudaSlice<f32>,
1575 n: usize,
1576 nrow: usize,
1577 temp: f32,
1578 top_k: i32,
1579 top_p: f32,
1580 min_p: f32,
1581 ) -> Result<(), Box<dyn std::error::Error>> {
1582 static COOP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1597 let coop_on =
1598 *COOP_ON.get_or_init(|| std::env::var("MEMRA_FILTER_COOP").as_deref() != Ok("0"));
1599 let (ni, nr, rs) = (n as i32, nrow as i32, row_stride as i64);
1600 if coop_on && 16 * nrow <= self.sm_count() as usize {
1601 let f = self.func("filter_stats_coop_f32");
1602 let mut ws = self.alloc_uninit::<f32>(nrow * (2 * 16 + 2))?;
1603 let cfg = LaunchConfig {
1604 grid_dim: (16, nrow as u32, 1),
1605 block_dim: (512, 1, 1),
1606 shared_mem_bytes: 0,
1607 };
1608 let __s_b = self.gpu.stream();
1609 let mut b = __s_b.launch_builder(&f);
1610 b.arg(x)
1611 .arg(&rs)
1612 .arg(rows)
1613 .arg(&mut *out_th)
1614 .arg(&mut *out_z)
1615 .arg(&mut *out_max)
1616 .arg(&mut ws)
1617 .arg(&ni)
1618 .arg(&nr)
1619 .arg(&temp)
1620 .arg(&top_k)
1621 .arg(&top_p)
1622 .arg(&min_p);
1623 unsafe {
1624 b.launch_cooperative(cfg)?;
1625 }
1626 return Ok(());
1627 }
1628 let f = self.func("filter_stats_f32");
1629 let cfg = LaunchConfig {
1630 grid_dim: (nrow as u32, 1, 1),
1631 block_dim: (1024, 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(x)
1637 .arg(&rs)
1638 .arg(rows)
1639 .arg(&mut *out_th)
1640 .arg(&mut *out_z)
1641 .arg(&mut *out_max)
1642 .arg(&ni)
1643 .arg(&nr)
1644 .arg(&temp)
1645 .arg(&top_k)
1646 .arg(&top_p)
1647 .arg(&min_p);
1648 unsafe {
1649 b.launch(cfg)?;
1650 }
1651 Ok(())
1652 }
1653
1654 #[allow(clippy::too_many_arguments)]
1656 pub fn softmax_gather_filtered(
1657 &self,
1658 x: &CudaSlice<f32>,
1659 row_stride: usize,
1660 ids: &CudaSlice<u32>,
1661 rows: &CudaSlice<i32>,
1662 th: &CudaSlice<f32>,
1663 z: &CudaSlice<f32>,
1664 out: &mut CudaSlice<f32>,
1665 n: usize,
1666 npair: usize,
1667 temp: f32,
1668 ) -> Result<(), Box<dyn std::error::Error>> {
1669 let f = self.func("softmax_gather_filtered_f32");
1670 let (ni, np, rs) = (n as i32, npair as i32, row_stride as i64);
1671 let cfg = LaunchConfig {
1672 grid_dim: (npair as u32, 1, 1),
1673 block_dim: (256, 1, 1),
1674 shared_mem_bytes: 0,
1675 };
1676 let __s_b = self.gpu.stream();
1677 let mut b = __s_b.launch_builder(&f);
1678 b.arg(x)
1679 .arg(&rs)
1680 .arg(ids)
1681 .arg(rows)
1682 .arg(th)
1683 .arg(z)
1684 .arg(&mut *out)
1685 .arg(&ni)
1686 .arg(&np)
1687 .arg(&temp);
1688 unsafe {
1689 b.launch(cfg)?;
1690 }
1691 Ok(())
1692 }
1693
1694 #[allow(clippy::too_many_arguments)]
1696 pub fn residual_sample_filtered(
1697 &self,
1698 p: &CudaSlice<f32>,
1699 q: Option<&CudaSlice<f32>>,
1700 n: usize,
1701 temp: f32,
1702 seed: u64,
1703 stream_pos: u32,
1704 p_stats: (f32, f32, f32),
1705 q_stats: (f32, f32, f32),
1706 out_tok: &mut CudaSlice<u32>,
1707 ) -> Result<(), Box<dyn std::error::Error>> {
1708 let f = self.func("residual_sample_filtered_f32");
1709 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1710 let has_q: i32 = q.is_some() as i32;
1711 let qbuf = q.unwrap_or(p);
1712 let (pm, pth, pz) = p_stats;
1713 let (qm, qth, qz) = q_stats;
1714 let cfg = LaunchConfig {
1715 grid_dim: (1, 1, 1),
1716 block_dim: (1024, 1, 1),
1717 shared_mem_bytes: 0,
1718 };
1719 let __s_b = self.gpu.stream();
1720 let mut b = __s_b.launch_builder(&f);
1721 b.arg(p)
1722 .arg(qbuf)
1723 .arg(&has_q)
1724 .arg(&ni)
1725 .arg(&temp)
1726 .arg(&slo)
1727 .arg(&shi)
1728 .arg(&stream_pos)
1729 .arg(&pm)
1730 .arg(&pth)
1731 .arg(&pz)
1732 .arg(&qm)
1733 .arg(&qth)
1734 .arg(&qz)
1735 .arg(&mut *out_tok);
1736 unsafe {
1737 b.launch(cfg)?;
1738 }
1739 Ok(())
1740 }
1741
1742 #[allow(clippy::too_many_arguments)]
1748 pub fn residual_sample_sparse_q(
1749 &self,
1750 p: &CudaSlice<f32>,
1751 cand_ids: &CudaSlice<u32>,
1752 q_probs: &CudaSlice<f32>,
1753 n_cand: usize,
1754 n: usize,
1755 temp: f32,
1756 seed: u64,
1757 stream_pos: u32,
1758 p_stats: (f32, f32, f32),
1759 out_tok: &mut CudaSlice<u32>,
1760 ) -> Result<(), Box<dyn std::error::Error>> {
1761 assert!(
1762 n_cand >= 1 && n_cand <= 32,
1763 "residual_sample_sparse_q supports 1..=32 candidates, got {n_cand}"
1764 );
1765 let f = self.func("residual_sample_sparse_q_f32");
1766 let (ni, nc) = (n as i32, n_cand as i32);
1767 let (slo, shi) = ((seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1768 let (pm, pth, pz) = p_stats;
1769 let cfg = LaunchConfig {
1770 grid_dim: (1, 1, 1),
1771 block_dim: (1024, 1, 1),
1772 shared_mem_bytes: 0,
1773 };
1774 let __s_b = self.gpu.stream();
1775 let mut b = __s_b.launch_builder(&f);
1776 b.arg(p)
1777 .arg(cand_ids)
1778 .arg(q_probs)
1779 .arg(&nc)
1780 .arg(&ni)
1781 .arg(&temp)
1782 .arg(&slo)
1783 .arg(&shi)
1784 .arg(&stream_pos)
1785 .arg(&pm)
1786 .arg(&pth)
1787 .arg(&pz)
1788 .arg(&mut *out_tok);
1789 unsafe {
1790 b.launch(cfg)?;
1791 }
1792 Ok(())
1793 }
1794
1795 #[allow(clippy::too_many_arguments)]
1797 pub fn gumbel_perturb_filtered(
1798 &self,
1799 x: &CudaSlice<f32>,
1800 y: &mut CudaSlice<f32>,
1801 n: usize,
1802 seed: u64,
1803 stream_pos: u32,
1804 temp: f32,
1805 row_max: f32,
1806 th: f32,
1807 ) -> Result<(), Box<dyn std::error::Error>> {
1808 let f = self.func("gumbel_perturb_filtered_f32");
1809 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
1810 let cfg = LaunchConfig {
1811 grid_dim: (n.div_ceil(256) as u32, 1, 1),
1812 block_dim: (256, 1, 1),
1813 shared_mem_bytes: 0,
1814 };
1815 let __s_b = self.gpu.stream();
1816 let mut b = __s_b.launch_builder(&f);
1817 b.arg(x)
1818 .arg(&mut *y)
1819 .arg(&ni)
1820 .arg(&slo)
1821 .arg(&shi)
1822 .arg(&stream_pos)
1823 .arg(&temp)
1824 .arg(&row_max)
1825 .arg(&th);
1826 unsafe {
1827 b.launch(cfg)?;
1828 }
1829 Ok(())
1830 }
1831
1832 #[allow(clippy::too_many_arguments)]
1836 pub fn penalize_logits(
1837 &self,
1838 x: &mut CudaSlice<f32>,
1839 hist: &CudaSlice<u32>,
1840 n_hist: usize,
1841 rep: f32,
1842 freq: f32,
1843 present: f32,
1844 n: usize,
1845 ) -> Result<(), Box<dyn std::error::Error>> {
1846 if n_hist == 0 {
1847 return Ok(());
1848 }
1849 let f = self.func("penalize_logits_f32");
1850 let (nh, ni) = (n_hist as i32, n as i32);
1851 let cfg = LaunchConfig {
1852 grid_dim: (n_hist.div_ceil(128) as u32, 1, 1),
1853 block_dim: (128, 1, 1),
1854 shared_mem_bytes: 0,
1855 };
1856 let __s_b = self.gpu.stream();
1857 let mut b = __s_b.launch_builder(&f);
1858 b.arg(&mut *x)
1859 .arg(hist)
1860 .arg(&nh)
1861 .arg(&rep)
1862 .arg(&freq)
1863 .arg(&present)
1864 .arg(&ni);
1865 unsafe {
1866 b.launch(cfg)?;
1867 }
1868 Ok(())
1869 }
1870
1871 #[allow(clippy::too_many_arguments)]
1873 pub fn penalize_logits_rows(
1874 &self,
1875 x: &mut CudaSlice<f32>,
1876 hist: &CudaSlice<u32>,
1877 n_hist: usize,
1878 rep: f32,
1879 freq: f32,
1880 present: f32,
1881 n: usize,
1882 nrow: usize,
1883 ) -> Result<(), Box<dyn std::error::Error>> {
1884 if n_hist == 0 || nrow == 0 {
1885 return Ok(());
1886 }
1887 let f = self.func("penalize_logits_rows_f32");
1888 let (nh, ni, nr) = (n_hist as i32, n as i32, nrow as i32);
1889 let cfg = LaunchConfig {
1890 grid_dim: (n_hist.div_ceil(128) as u32, nrow as u32, 1),
1891 block_dim: (128, 1, 1),
1892 shared_mem_bytes: 0,
1893 };
1894 let __s_b = self.gpu.stream();
1895 let mut b = __s_b.launch_builder(&f);
1896 b.arg(&mut *x)
1897 .arg(hist)
1898 .arg(&nh)
1899 .arg(&rep)
1900 .arg(&freq)
1901 .arg(&present)
1902 .arg(&ni)
1903 .arg(&nr);
1904 unsafe {
1905 b.launch(cfg)?;
1906 }
1907 Ok(())
1908 }
1909
1910 #[allow(clippy::too_many_arguments)]
1918 pub fn penalize_logits_rows_inc(
1919 &self,
1920 x: &mut CudaSlice<f32>,
1921 hist: &CudaSlice<u32>,
1922 n_hist0: usize,
1923 rep: f32,
1924 freq: f32,
1925 present: f32,
1926 n: usize,
1927 nrow: usize,
1928 win: usize,
1929 ) -> Result<(), Box<dyn std::error::Error>> {
1930 if nrow == 0 || win == 0 || (n_hist0 == 0 && nrow == 1) {
1931 return Ok(());
1932 }
1933 debug_assert!(
1934 hist.len() >= n_hist0 + nrow - 1,
1935 "rows-inc hist must carry n_hist0 + nrow - 1 ids"
1936 );
1937 let f = self.func("penalize_logits_rows_inc_f32");
1938 let max_len = win.min(n_hist0 + nrow - 1).max(1);
1939 let (nh, ni, nr, wi) = (n_hist0 as i32, n as i32, nrow as i32, win as i32);
1940 let cfg = LaunchConfig {
1941 grid_dim: (max_len.div_ceil(128) as u32, nrow as u32, 1),
1942 block_dim: (128, 1, 1),
1943 shared_mem_bytes: 0,
1944 };
1945 let __s_b = self.gpu.stream();
1946 let mut b = __s_b.launch_builder(&f);
1947 b.arg(&mut *x)
1948 .arg(hist)
1949 .arg(&nh)
1950 .arg(&rep)
1951 .arg(&freq)
1952 .arg(&present)
1953 .arg(&ni)
1954 .arg(&nr)
1955 .arg(&wi);
1956 unsafe {
1957 b.launch(cfg)?;
1958 }
1959 Ok(())
1960 }
1961
1962 pub fn wpf_level() -> u32 {
1970 static ON: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
1971 *ON.get_or_init(|| {
1972 std::env::var("MEMRA_WPF")
1973 .ok()
1974 .and_then(|v| v.parse().ok())
1975 .unwrap_or(1)
1976 })
1977 }
1978
1979 pub fn set_verify_exact(&self, on: bool) {
1991 self.verify_exact
1992 .store(on, std::sync::atomic::Ordering::Relaxed);
1993 }
1994 pub(crate) fn verify_exact_on(&self) -> bool {
1995 self.verify_exact.load(std::sync::atomic::Ordering::Relaxed)
1996 }
1997
1998 pub fn qkv_append_on() -> bool {
2001 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2002 *ON.get_or_init(|| {
2003 std::env::var("MEMRA_QKV_APPEND")
2004 .map(|v| v != "0")
2005 .unwrap_or(true)
2006 })
2007 }
2008
2009 pub fn pdl_wb_on() -> bool {
2012 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2013 *ON.get_or_init(|| {
2014 std::env::var("MEMRA_PDL_WB")
2015 .map(|v| v != "0")
2016 .unwrap_or(true)
2017 })
2018 }
2019
2020 pub fn norm_ilp_on() -> bool {
2028 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2029 *ON.get_or_init(|| {
2030 std::env::var("MEMRA_NORM_ILP")
2031 .map(|v| v != "0")
2032 .unwrap_or(true)
2033 })
2034 }
2035
2036 pub fn tk_ffn_dual_on() -> bool {
2044 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2045 *ON.get_or_init(|| {
2046 std::env::var("MEMRA_TK_FFN_DUAL")
2047 .map(|v| v != "0")
2048 .unwrap_or(true)
2049 })
2050 }
2051
2052 pub fn pdl_mmvq_on() -> bool {
2056 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2057 *ON.get_or_init(|| {
2058 std::env::var("MEMRA_PDL_MMVQ")
2059 .map(|v| v != "0")
2060 .unwrap_or(true)
2061 })
2062 }
2063
2064 pub fn pdl_on() -> bool {
2065 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2066 *ON.get_or_init(|| std::env::var("MEMRA_PDL").map(|v| v != "0").unwrap_or(true))
2067 }
2068
2069 pub fn pdl_nvfp4q8_on() -> bool {
2075 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2076 *ON.get_or_init(|| {
2077 std::env::var("MEMRA_PDL_NVFP4")
2078 .map(|v| v != "0")
2079 .unwrap_or(true)
2080 })
2081 }
2082
2083 fn q40_mr1_on() -> bool {
2089 static Q40MR: std::sync::OnceLock<Option<u32>> = std::sync::OnceLock::new();
2090 match *Q40MR.get_or_init(|| {
2091 std::env::var("MEMRA_Q40_MR")
2092 .ok()
2093 .and_then(|v| v.parse().ok())
2094 }) {
2095 Some(v) => v == 1,
2096 None => crate::FUSED_MR1_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
2097 }
2098 }
2099
2100 fn pdl_func_flash(
2105 &self,
2106 g: bool,
2107 name: &'static str,
2108 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
2109 use cudarc::driver::sys as cu;
2110 static MODS: std::sync::Mutex<Option<std::collections::HashMap<(usize, bool), usize>>> =
2117 std::sync::Mutex::new(None);
2118 static FNS: std::sync::Mutex<
2119 Option<std::collections::HashMap<(usize, bool, &'static str), usize>>,
2120 > = std::sync::Mutex::new(None);
2121 let ctx_key = self.ctx().cu_ctx() as usize;
2122 if let Some(&f) = FNS
2123 .lock()
2124 .unwrap()
2125 .get_or_insert_with(Default::default)
2126 .get(&(ctx_key, g, name))
2127 {
2128 return Ok(f as cu::CUfunction);
2129 }
2130 let module = {
2131 let mut mods = MODS.lock().unwrap();
2132 let map = mods.get_or_insert_with(Default::default);
2133 match map.get(&(ctx_key, g)) {
2134 Some(&m) => m,
2135 None => {
2136 let m = self.pdl_load_module_in_ctx(if g {
2137 FLASH_FATBIN_KF8VF8
2138 } else {
2139 FLASH_FATBIN
2140 })?;
2141 map.insert((ctx_key, g), m);
2142 m
2143 }
2144 }
2145 };
2146 let cname = std::ffi::CString::new(name)?;
2147 let mut f: cu::CUfunction = std::ptr::null_mut();
2148 let r = unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
2149 if r != cu::CUresult::CUDA_SUCCESS {
2150 return Err(format!("pdl_func_flash {name} (g={g}): {r:?}").into());
2151 }
2152 FNS.lock()
2153 .unwrap()
2154 .get_or_insert_with(Default::default)
2155 .insert((ctx_key, g, name), f as usize);
2156 Ok(f)
2157 }
2158
2159 fn pdl_load_module_in_ctx(&self, bytes: &[u8]) -> Result<usize, Box<dyn std::error::Error>> {
2164 use cudarc::driver::sys as cu;
2165 let mut prev: cu::CUcontext = std::ptr::null_mut();
2166 unsafe {
2167 cu::cuCtxGetCurrent(&mut prev).result()?;
2168 }
2169 self.ctx().bind_to_thread()?;
2170 let mut m: cu::CUmodule = std::ptr::null_mut();
2171 let r = unsafe { cu::cuModuleLoadData(&mut m, bytes.as_ptr() as *const std::ffi::c_void) };
2172 let restore = if prev.is_null() {
2173 cu::CUresult::CUDA_SUCCESS
2174 } else {
2175 unsafe { cu::cuCtxSetCurrent(prev) }
2176 };
2177 if r != cu::CUresult::CUDA_SUCCESS {
2178 return Err(format!("pdl module load: {r:?}").into());
2179 }
2180 if restore != cu::CUresult::CUDA_SUCCESS {
2181 return Err(format!("pdl module load: ctx restore {restore:?}").into());
2182 }
2183 Ok(m as usize)
2184 }
2185
2186 pub fn raw_kernel_function(
2189 &self,
2190 name: &'static str,
2191 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
2192 self.pdl_func(name)
2193 }
2194
2195 fn pdl_func(
2196 &self,
2197 name: &'static str,
2198 ) -> Result<cudarc::driver::sys::CUfunction, Box<dyn std::error::Error>> {
2199 use cudarc::driver::sys as cu;
2200 static MODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
2203 std::sync::Mutex::new(None);
2204 static QMODULES: std::sync::Mutex<Option<std::collections::HashMap<usize, usize>>> =
2207 std::sync::Mutex::new(None);
2208 static FNS: std::sync::Mutex<
2209 Option<std::collections::HashMap<(usize, &'static str), usize>>,
2210 > = std::sync::Mutex::new(None);
2211 let ctx_key = self.ctx().cu_ctx() as usize;
2212 if let Some(&f) = FNS
2213 .lock()
2214 .unwrap()
2215 .get_or_insert_with(Default::default)
2216 .get(&(ctx_key, name))
2217 {
2218 return Ok(f as cu::CUfunction);
2219 }
2220 let module = {
2221 let mut mods = MODULES.lock().unwrap();
2222 let map = mods.get_or_insert_with(Default::default);
2223 match map.get(&ctx_key) {
2224 Some(&m) => m,
2225 None => {
2226 let m = self.pdl_load_module_in_ctx(FATBIN)?;
2227 map.insert(ctx_key, m);
2228 m
2229 }
2230 }
2231 };
2232 let cname = std::ffi::CString::new(name)?;
2233 let mut f: cu::CUfunction = std::ptr::null_mut();
2234 let mut r =
2235 unsafe { cu::cuModuleGetFunction(&mut f, module as cu::CUmodule, cname.as_ptr()) };
2236 if r == cu::CUresult::CUDA_ERROR_NOT_FOUND {
2237 let qmodule = {
2238 let mut mods = QMODULES.lock().unwrap();
2239 let map = mods.get_or_insert_with(Default::default);
2240 match map.get(&ctx_key) {
2241 Some(&m) => m,
2242 None => {
2243 let m = self.pdl_load_module_in_ctx(QMATVEC_FATBIN)?;
2244 map.insert(ctx_key, m);
2245 m
2246 }
2247 }
2248 };
2249 r = unsafe { cu::cuModuleGetFunction(&mut f, qmodule as cu::CUmodule, cname.as_ptr()) };
2250 }
2251 if r != cu::CUresult::CUDA_SUCCESS {
2252 return Err(format!("pdl_func {name}: {r:?}").into());
2253 }
2254 FNS.lock()
2255 .unwrap()
2256 .get_or_insert_with(Default::default)
2257 .insert((ctx_key, name), f as usize);
2258 Ok(f)
2259 }
2260
2261 unsafe fn launch_pdl_flash(
2273 &self,
2274 g: bool,
2275 name: &'static str,
2276 grid: (u32, u32, u32),
2277 block: (u32, u32, u32),
2278 smem: u32,
2279 params: &mut [*mut std::ffi::c_void],
2280 ) -> Result<(), Box<dyn std::error::Error>> {
2281 use cudarc::driver::sys as cu;
2282 let f = self.pdl_func_flash(g, name)?;
2283 if smem > 0 {
2284 let r =
2286 unsafe {
2287 cu::cuFuncSetAttribute(f,
2288 cu::CUfunction_attribute_enum::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
2289 smem as i32)
2290 };
2291 if r != cu::CUresult::CUDA_SUCCESS {
2292 return Err(format!("pdl smem attr {name}: {r:?}").into());
2293 }
2294 }
2295 let mut attr = cu::CUlaunchAttribute {
2296 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
2297 pad: [0; 4],
2298 value: cu::CUlaunchAttributeValue {
2299 programmaticStreamSerializationAllowed: 1,
2300 },
2301 };
2302 let cfg = cu::CUlaunchConfig {
2303 gridDimX: grid.0,
2304 gridDimY: grid.1,
2305 gridDimZ: grid.2,
2306 blockDimX: block.0,
2307 blockDimY: block.1,
2308 blockDimZ: block.2,
2309 sharedMemBytes: smem,
2310 hStream: self.gpu.stream().cu_stream(),
2311 attrs: &mut attr,
2312 numAttrs: 1,
2313 };
2314 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
2315 if r != cu::CUresult::CUDA_SUCCESS {
2316 return Err(format!("launch_pdl_flash {name}: {r:?}").into());
2317 }
2318 Ok(())
2319 }
2320
2321 unsafe fn launch_pdl(
2322 &self,
2323 name: &'static str,
2324 grid: (u32, u32, u32),
2325 block: (u32, u32, u32),
2326 params: &mut [*mut std::ffi::c_void],
2327 ) -> Result<(), Box<dyn std::error::Error>> {
2328 use cudarc::driver::sys as cu;
2329 let f = self.pdl_func(name)?;
2330 let mut attr = cu::CUlaunchAttribute {
2331 id: cu::CUlaunchAttributeID::CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION,
2332 pad: [0; 4],
2333 value: cu::CUlaunchAttributeValue {
2334 programmaticStreamSerializationAllowed: 1,
2335 },
2336 };
2337 let cfg = cu::CUlaunchConfig {
2338 gridDimX: grid.0,
2339 gridDimY: grid.1,
2340 gridDimZ: grid.2,
2341 blockDimX: block.0,
2342 blockDimY: block.1,
2343 blockDimZ: block.2,
2344 sharedMemBytes: 0,
2345 hStream: self.gpu.stream().cu_stream(),
2346 attrs: &mut attr,
2347 numAttrs: 1,
2348 };
2349 let r = unsafe { cu::cuLaunchKernelEx(&cfg, f, params.as_mut_ptr(), std::ptr::null_mut()) };
2350 if r != cu::CUresult::CUDA_SUCCESS {
2351 return Err(format!("launch_pdl {name}: {r:?}").into());
2352 }
2353 Ok(())
2354 }
2355
2356 pub fn prefetch_weight_l2(
2359 &self,
2360 w: &crate::model::GpuTensor,
2361 ) -> Result<(), Box<dyn std::error::Error>> {
2362 if let crate::model::GpuTensor::Quant { bytes, rp4, .. } = w {
2363 let p = rp4.as_ref().unwrap_or(bytes);
2364 self.prefetch_l2(p, p.len())?;
2365 }
2366 Ok(())
2367 }
2368
2369 pub fn gather_row_bf16(
2372 &self,
2373 table: &CudaSlice<u8>,
2374 tok: &CudaSlice<u32>,
2375 idx: usize,
2376 dst: &mut CudaSlice<f32>,
2377 ncols: usize,
2378 ) -> Result<(), Box<dyn std::error::Error>> {
2379 let f = self.func("gather_row_bf16_f32");
2380 let cfg = LaunchConfig {
2381 grid_dim: (ncols.div_ceil(256) as u32, 1, 1),
2382 block_dim: (256, 1, 1),
2383 shared_mem_bytes: 0,
2384 };
2385 let (nc, ix) = (ncols as i32, idx as i32);
2386 let __s_b = self.gpu.stream();
2387 let mut b = __s_b.launch_builder(&f);
2388 b.arg(table).arg(tok).arg(&ix).arg(dst).arg(&nc);
2389 unsafe {
2390 b.launch(cfg)?;
2391 }
2392 Ok(())
2393 }
2394
2395 #[allow(clippy::too_many_arguments)]
2401 pub fn dflash2_dynconv(
2402 &self,
2403 x: &CudaSlice<f32>,
2404 dyn_: &CudaSlice<f32>,
2405 base: &CudaSlice<f32>,
2406 out: &mut CudaSlice<f32>,
2407 rows: usize,
2408 hidden: usize,
2409 group_size: usize,
2410 ksize: usize,
2411 half: usize,
2412 ) -> Result<(), Box<dyn std::error::Error>> {
2413 assert_eq!(hidden % group_size, 0, "hidden % group_size != 0");
2414 let f = self.func("dflash2_dynconv_f32");
2415 let n = rows * hidden;
2416 let cfg = LaunchConfig {
2417 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2418 block_dim: (256, 1, 1),
2419 shared_mem_bytes: 0,
2420 };
2421 let (ri, hi, gi, ki, hf) = (
2422 rows as i32,
2423 hidden as i32,
2424 group_size as i32,
2425 ksize as i32,
2426 half as i32,
2427 );
2428 let __s_b = self.gpu.stream();
2429 let mut b = __s_b.launch_builder(&f);
2430 b.arg(x)
2431 .arg(dyn_)
2432 .arg(base)
2433 .arg(out)
2434 .arg(&ri)
2435 .arg(&hi)
2436 .arg(&gi)
2437 .arg(&ki)
2438 .arg(&hf);
2439 unsafe {
2440 b.launch(cfg)?;
2441 }
2442 Ok(())
2443 }
2444
2445 pub fn topk_rows(
2449 &self,
2450 logits: &CudaSlice<f32>,
2451 n_rows: usize,
2452 n_cols: usize,
2453 k: usize,
2454 ) -> Result<(CudaSlice<f32>, CudaSlice<u32>), Box<dyn std::error::Error>> {
2455 assert!(k <= 32 && k >= 1, "topk_rows supports 1..=32, got {k}");
2456 assert!(k <= n_cols, "topk_rows: k {k} > n_cols {n_cols}");
2457 let f = self.func("topk_rows_f32");
2458 let nth = 256usize;
2459 let mut vals = self.uninit(n_rows * k)?;
2460 let mut idxs = self.gpu.stream().alloc_zeros::<u32>(n_rows * k)?;
2461 let cfg = LaunchConfig {
2462 grid_dim: (n_rows as u32, 1, 1),
2463 block_dim: (nth as u32, 1, 1),
2464 shared_mem_bytes: (nth * k * 8) as u32,
2465 };
2466 let (nr, nc, ki) = (n_rows as i32, n_cols as i32, k as i32);
2467 let __s_b = self.gpu.stream();
2468 let mut b = __s_b.launch_builder(&f);
2469 b.arg(logits)
2470 .arg(&nr)
2471 .arg(&nc)
2472 .arg(&ki)
2473 .arg(&mut vals)
2474 .arg(&mut idxs);
2475 unsafe {
2476 b.launch(cfg)?;
2477 }
2478 Ok((vals, idxs))
2479 }
2480
2481 pub fn add_row_inplace(
2483 &self,
2484 logits: &mut CudaSlice<f32>,
2485 bias: &CudaSlice<f32>,
2486 n: usize,
2487 row_off: usize,
2488 ) -> Result<(), Box<dyn std::error::Error>> {
2489 let f = self.func("add_row_inplace_f32");
2490 let cfg = LaunchConfig {
2491 grid_dim: (n.div_ceil(256) as u32, 1, 1),
2492 block_dim: (256, 1, 1),
2493 shared_mem_bytes: 0,
2494 };
2495 let (ni, off) = (n as i32, row_off as i64);
2496 let __s_b = self.gpu.stream();
2497 let mut b = __s_b.launch_builder(&f);
2498 b.arg(logits).arg(bias).arg(&ni).arg(&off);
2499 unsafe {
2500 b.launch(cfg)?;
2501 }
2502 Ok(())
2503 }
2504
2505 pub fn prefetch_l2(
2507 &self,
2508 p: &CudaSlice<u8>,
2509 n: usize,
2510 ) -> Result<(), Box<dyn std::error::Error>> {
2511 let f = self.func("prefetch_l2_bytes");
2512 let lines = n.div_ceil(128);
2513 let ni = n as i64;
2514 let cfg = LaunchConfig {
2515 grid_dim: (lines.div_ceil(256) as u32, 1, 1),
2516 block_dim: (256, 1, 1),
2517 shared_mem_bytes: 0,
2518 };
2519 let __s_b = self.gpu.stream();
2520 let mut b = __s_b.launch_builder(&f);
2521 b.arg(p).arg(&ni);
2522 unsafe {
2523 b.launch(cfg)?;
2524 }
2525 Ok(())
2526 }
2527
2528 pub fn router_gemv(
2531 &self,
2532 w: &CudaSlice<f32>,
2533 x: &CudaSlice<f32>,
2534 n_embd: usize,
2535 n_experts: usize,
2536 t: usize,
2537 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2538 let w8 = match std::env::var("MEMRA_ROUTER_V2").as_deref() {
2544 Ok("0") => false,
2545 Ok(_) => true,
2546 Err(_) => ROUTER_W8_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
2547 };
2548 let batch = w8 && t >= ROUTER_BATCH_MIN_T && router_batch_on();
2558 self.router_gemv_form(w, x, n_embd, n_experts, t, w8, batch)
2559 }
2560
2561 pub fn router_gemv_form(
2564 &self,
2565 w: &CudaSlice<f32>,
2566 x: &CudaSlice<f32>,
2567 n_embd: usize,
2568 n_experts: usize,
2569 t: usize,
2570 w8: bool,
2571 batch: bool,
2572 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2573 debug_assert!(!batch || w8, "batch twin exists for the w8 form only");
2574 let mut y = self.alloc_uninit::<f32>(t * n_experts)?;
2575 let f = if batch {
2576 self.func("router_gemv_f32_w8_batch")
2577 } else if w8 {
2578 self.func("router_gemv_f32_w8")
2579 } else {
2580 self.func("router_gemv_f32")
2581 };
2582 let (ne, nx, ti) = (n_embd as i32, n_experts as i32, t as i32);
2583 let cfg = if batch {
2584 LaunchConfig {
2585 grid_dim: (n_experts.div_ceil(8) as u32, t.div_ceil(8) as u32, 1),
2586 block_dim: (32, 8, 1),
2587 shared_mem_bytes: 0,
2588 }
2589 } else {
2590 LaunchConfig {
2591 grid_dim: (n_experts as u32, t as u32, 1),
2592 block_dim: (32, if w8 { 8 } else { 1 }, 1),
2593 shared_mem_bytes: 0,
2594 }
2595 };
2596 let __s_b = self.gpu.stream();
2597 let mut b = __s_b.launch_builder(&f);
2598 b.arg(w).arg(x).arg(&mut y).arg(&ne).arg(&nx).arg(&ti);
2599 unsafe {
2600 b.launch(cfg)?;
2601 }
2602 Ok(y)
2603 }
2604
2605 pub fn router_gemv_into(
2608 &self,
2609 w: &CudaSlice<f32>,
2610 x: &CudaSlice<f32>,
2611 y: &mut CudaSlice<f32>,
2612 n_embd: usize,
2613 n_experts: usize,
2614 t: usize,
2615 ) -> Result<(), Box<dyn std::error::Error>> {
2616 if y.len() < t * n_experts {
2617 return Err("router_gemv_into output too small".into());
2618 }
2619 let w8 = match std::env::var("MEMRA_ROUTER_V2").as_deref() {
2620 Ok("0") => false,
2621 Ok(_) => true,
2622 Err(_) => ROUTER_W8_DEFAULT.load(std::sync::atomic::Ordering::Relaxed),
2623 };
2624 let f = if w8 {
2625 self.func("router_gemv_f32_w8")
2626 } else {
2627 self.func("router_gemv_f32")
2628 };
2629 let (ne, nx, ti) = (n_embd as i32, n_experts as i32, t as i32);
2630 let cfg = LaunchConfig {
2631 grid_dim: (n_experts as u32, t as u32, 1),
2632 block_dim: (32, if w8 { 8 } else { 1 }, 1),
2633 shared_mem_bytes: 0,
2634 };
2635 let __s_b = self.gpu.stream();
2636 let mut b = __s_b.launch_builder(&f);
2637 b.arg(w).arg(x).arg(&mut *y).arg(&ne).arg(&nx).arg(&ti);
2638 unsafe {
2639 b.launch(cfg)?;
2640 }
2641 Ok(())
2642 }
2643
2644 pub fn rows_permute(
2646 &self,
2647 src: &CudaSlice<f32>,
2648 idx: &CudaSlice<i32>,
2649 nrows: usize,
2650 ncols: usize,
2651 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2652 let mut dst = self.alloc_uninit::<f32>(nrows * ncols)?;
2653 let f = self.func("rows_permute_f32");
2654 let (nc, nr) = (ncols as i32, nrows as i32);
2655 let cfg = LaunchConfig {
2656 grid_dim: (nrows as u32, 1, 1),
2657 block_dim: (256, 1, 1),
2658 shared_mem_bytes: 0,
2659 };
2660 let __s_b = self.gpu.stream();
2661 let mut b = __s_b.launch_builder(&f);
2662 b.arg(src).arg(idx).arg(&mut dst).arg(&nc).arg(&nr);
2663 unsafe {
2664 b.launch(cfg)?;
2665 }
2666 Ok(dst)
2667 }
2668
2669 pub fn sigmoid_dot_rows(
2674 &self,
2675 x: &CudaSlice<f32>,
2676 w: &CudaSlice<f32>,
2677 n_embd: usize,
2678 t: usize,
2679 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
2680 static OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2683 if *OFF.get_or_init(|| std::env::var("MEMRA_SHEXP_DOT").as_deref() == Ok("0")) {
2684 let gs = self.linear(x, w, t, n_embd, 1)?;
2685 let mut g = self.uninit(t)?;
2686 self.sigmoid(&gs, &mut g, t)?;
2687 return Ok(g);
2688 }
2689 let mut g = self.alloc_uninit::<f32>(t)?;
2695 let f = self.func("sigmoid_dot_rows_f32");
2696 let (ne, ti) = (n_embd as i32, t as i32);
2697 let cfg = LaunchConfig {
2698 grid_dim: (t as u32, 1, 1),
2699 block_dim: (32, 8, 1),
2700 shared_mem_bytes: 0,
2701 };
2702 let __s_b = self.gpu.stream();
2703 let mut b = __s_b.launch_builder(&f);
2704 b.arg(x).arg(w).arg(&mut g).arg(&ne).arg(&ti);
2705 unsafe {
2706 b.launch(cfg)?;
2707 }
2708 Ok(g)
2709 }
2710
2711 pub fn sigmoid_dot_rows_into(
2713 &self,
2714 x: &CudaSlice<f32>,
2715 w: &CudaSlice<f32>,
2716 g: &mut CudaSlice<f32>,
2717 n_embd: usize,
2718 t: usize,
2719 ) -> Result<(), Box<dyn std::error::Error>> {
2720 if g.len() < t {
2721 return Err("sigmoid_dot_rows_into output too small".into());
2722 }
2723 let f = self.func("sigmoid_dot_rows_f32");
2724 let (ne, ti) = (n_embd as i32, t as i32);
2725 let cfg = LaunchConfig {
2726 grid_dim: (t as u32, 1, 1),
2727 block_dim: (32, 8, 1),
2728 shared_mem_bytes: 0,
2729 };
2730 let __s_b = self.gpu.stream();
2731 let mut b = __s_b.launch_builder(&f);
2732 b.arg(x).arg(w).arg(&mut *g).arg(&ne).arg(&ti);
2733 unsafe {
2734 b.launch(cfg)?;
2735 }
2736 Ok(())
2737 }
2738
2739 pub fn spec_rollback_stream(
2741 &self,
2742 len_ptrs: &CudaSlice<u64>,
2743 pos_start: &CudaSlice<i32>,
2744 acc: &CudaSlice<u32>,
2745 base: usize,
2746 n_rows: usize,
2747 ) -> Result<(), Box<dyn std::error::Error>> {
2748 let f = self.func("spec_rollback_stream");
2749 let (b, nr) = (base as i32, n_rows as i32);
2750 let cfg = LaunchConfig {
2751 grid_dim: (n_rows.div_ceil(64) as u32, 1, 1),
2752 block_dim: (64, 1, 1),
2753 shared_mem_bytes: 0,
2754 };
2755 let __s_bl = self.gpu.stream();
2756 let mut bl = __s_bl.launch_builder(&f);
2757 bl.arg(len_ptrs).arg(pos_start).arg(acc).arg(&b).arg(&nr);
2758 unsafe {
2759 bl.launch(cfg)?;
2760 }
2761 Ok(())
2762 }
2763
2764 pub fn plain_tok_ring(
2766 &self,
2767 vam: &CudaSlice<u32>,
2768 pos_start: &CudaSlice<i32>,
2769 base: usize,
2770 ring: &mut CudaSlice<u32>,
2771 ) -> Result<(), Box<dyn std::error::Error>> {
2772 let f = self.func("plain_tok_ring");
2773 let (b, cap) = (base as i32, ring.len() as i32);
2774 let cfg = LaunchConfig {
2775 grid_dim: (1, 1, 1),
2776 block_dim: (32, 1, 1),
2777 shared_mem_bytes: 0,
2778 };
2779 let __s_bl = self.gpu.stream();
2780 let mut bl = __s_bl.launch_builder(&f);
2781 bl.arg(vam).arg(pos_start).arg(&b).arg(&mut *ring).arg(&cap);
2782 unsafe {
2783 bl.launch(cfg)?;
2784 }
2785 Ok(())
2786 }
2787
2788 pub fn spec_ring_commit(
2790 &self,
2791 vtok: &CudaSlice<u32>,
2792 acc: &CudaSlice<u32>,
2793 brk: &CudaSlice<u32>,
2794 ring: &mut CudaSlice<u32>,
2795 pend: &mut CudaSlice<u32>,
2796 ) -> Result<(), Box<dyn std::error::Error>> {
2797 let f = self.func("spec_ring_commit");
2798 let cfg = LaunchConfig {
2799 grid_dim: (1, 1, 1),
2800 block_dim: (32, 1, 1),
2801 shared_mem_bytes: 0,
2802 };
2803 let __s_b = self.gpu.stream();
2804 let mut b = __s_b.launch_builder(&f);
2805 b.arg(vtok).arg(acc).arg(brk).arg(ring).arg(pend);
2806 unsafe {
2807 b.launch(cfg)?;
2808 }
2809 Ok(())
2810 }
2811 pub fn i32_copy_add(
2812 &self,
2813 src: &CudaSlice<i32>,
2814 dst: &mut CudaSlice<i32>,
2815 delta: i32,
2816 ) -> Result<(), Box<dyn std::error::Error>> {
2817 let f = self.func("i32_copy_add");
2818 let cfg = LaunchConfig {
2819 grid_dim: (1, 1, 1),
2820 block_dim: (32, 1, 1),
2821 shared_mem_bytes: 0,
2822 };
2823 let __s_b = self.gpu.stream();
2824 let mut b = __s_b.launch_builder(&f);
2825 b.arg(src).arg(dst).arg(&delta);
2826 unsafe {
2827 b.launch(cfg)?;
2828 }
2829 Ok(())
2830 }
2831 pub fn u32_copy(
2832 &self,
2833 src: &CudaSlice<u32>,
2834 dst: &mut CudaSlice<u32>,
2835 ) -> Result<(), Box<dyn std::error::Error>> {
2836 let f = self.func("u32_copy");
2837 let cfg = LaunchConfig {
2838 grid_dim: (1, 1, 1),
2839 block_dim: (32, 1, 1),
2840 shared_mem_bytes: 0,
2841 };
2842 let __s_b = self.gpu.stream();
2843 let mut b = __s_b.launch_builder(&f);
2844 b.arg(src).arg(dst);
2845 unsafe {
2846 b.launch(cfg)?;
2847 }
2848 Ok(())
2849 }
2850
2851 pub fn spec_adapt_k(
2855 &self,
2856 acc: &CudaSlice<u32>,
2857 brk: &mut CudaSlice<u32>,
2858 floor: usize,
2859 cap: usize,
2860 ) -> Result<(), Box<dyn std::error::Error>> {
2861 let f = self.func("spec_adapt_k");
2862 let (fl, cp) = (floor as i32, cap as i32);
2863 let cfg = LaunchConfig {
2864 grid_dim: (1, 1, 1),
2865 block_dim: (32, 1, 1),
2866 shared_mem_bytes: 0,
2867 };
2868 let __s_b = self.gpu.stream();
2869 let mut b = __s_b.launch_builder(&f);
2870 b.arg(acc).arg(brk).arg(&fl).arg(&cp);
2871 unsafe {
2872 b.launch(cfg)?;
2873 }
2874 Ok(())
2875 }
2876
2877 pub fn spec_accept_greedy_dc(
2879 &self,
2880 preds: &CudaSlice<u32>,
2881 vtok: &CudaSlice<u32>,
2882 last_pred: &CudaSlice<u32>,
2883 brk: &CudaSlice<u32>,
2884 out: &mut CudaSlice<u32>,
2885 ) -> Result<(), Box<dyn std::error::Error>> {
2886 let f = self.func("spec_accept_greedy_dc");
2887 let cfg = LaunchConfig {
2888 grid_dim: (1, 1, 1),
2889 block_dim: (32, 1, 1),
2890 shared_mem_bytes: 0,
2891 };
2892 let __s_b = self.gpu.stream();
2893 let mut b = __s_b.launch_builder(&f);
2894 b.arg(preds).arg(vtok).arg(last_pred).arg(brk).arg(out);
2895 unsafe {
2896 b.launch(cfg)?;
2897 }
2898 Ok(())
2899 }
2900
2901 pub fn pos_iota(
2903 &self,
2904 pos0: &CudaSlice<i32>,
2905 out: &mut CudaSlice<i32>,
2906 t: usize,
2907 ) -> Result<(), Box<dyn std::error::Error>> {
2908 let f = self.func("pos_iota_i32");
2909 let ti = t as i32;
2910 let cfg = LaunchConfig {
2911 grid_dim: (1, 1, 1),
2912 block_dim: (t.max(1) as u32, 1, 1),
2913 shared_mem_bytes: 0,
2914 };
2915 let __s_b = self.gpu.stream();
2916 let mut b = __s_b.launch_builder(&f);
2917 b.arg(pos0).arg(out).arg(&ti);
2918 unsafe {
2919 b.launch(cfg)?;
2920 }
2921 Ok(())
2922 }
2923 #[allow(clippy::too_many_arguments)]
2924 pub fn append_kv_quantized_rows_dc(
2925 &self,
2926 k_rows: &CudaSlice<f32>,
2927 v_rows: &CudaSlice<f32>,
2928 kc: &mut CudaSlice<u8>,
2929 vc: &mut CudaSlice<u8>,
2930 t0_dev: &CudaSlice<i32>,
2931 t: usize,
2932 kv_dim_k: usize,
2933 kv_dim_v: usize,
2934 k_tok_bytes: usize,
2935 v_tok_bytes: usize,
2936 g: bool,
2937 ) -> Result<(), Box<dyn std::error::Error>> {
2938 let f = if g {
2939 self.func_g("append_quantize_kv_q8_0_q5_1_rows_dc")
2940 } else {
2941 self.func("append_quantize_kv_q8_0_q5_1_rows_dc")
2942 };
2943 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
2944 let cfg = LaunchConfig {
2945 grid_dim: (nblk, t as u32, 1),
2946 block_dim: (32, 1, 1),
2947 shared_mem_bytes: 0,
2948 };
2949 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2950 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2951 let __s_b = self.gpu.stream();
2952 let mut b = __s_b.launch_builder(&f);
2953 b.arg(k_rows)
2954 .arg(v_rows)
2955 .arg(kc)
2956 .arg(vc)
2957 .arg(t0_dev)
2958 .arg(&kdk)
2959 .arg(&kdv)
2960 .arg(&ktb)
2961 .arg(&vtb);
2962 unsafe {
2963 b.launch(cfg)?;
2964 }
2965 Ok(())
2966 }
2967
2968 #[allow(clippy::too_many_arguments)]
2971 pub fn append_kv_quantized_row_dc_inc(
2972 &self,
2973 k_row: &CudaSlice<f32>,
2974 v_row: &CudaSlice<f32>,
2975 kc: &mut CudaSlice<u8>,
2976 vc: &mut CudaSlice<u8>,
2977 t0_dev: &mut CudaSlice<i32>,
2978 kv_dim_k: usize,
2979 kv_dim_v: usize,
2980 k_tok_bytes: usize,
2981 v_tok_bytes: usize,
2982 g: bool,
2983 ) -> Result<(), Box<dyn std::error::Error>> {
2984 let f = if g {
2985 self.func_g("append_quantize_kv_q8_0_q5_1_dc_inc")
2986 } else {
2987 self.func("append_quantize_kv_q8_0_q5_1_dc_inc")
2988 };
2989 let nthreads = ((kv_dim_k.max(kv_dim_v) / 32) * 32).min(1024) as u32;
2990 let cfg = LaunchConfig {
2991 grid_dim: (1, 1, 1),
2992 block_dim: (nthreads, 1, 1),
2993 shared_mem_bytes: 0,
2994 };
2995 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
2996 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
2997 let __s_b = self.gpu.stream();
2998 let mut b = __s_b.launch_builder(&f);
2999 b.arg(k_row)
3000 .arg(v_row)
3001 .arg(kc)
3002 .arg(vc)
3003 .arg(t0_dev)
3004 .arg(&kdk)
3005 .arg(&kdv)
3006 .arg(&ktb)
3007 .arg(&vtb);
3008 unsafe {
3009 b.launch(cfg)?;
3010 }
3011 Ok(())
3012 }
3013
3014 pub fn pack_tok_p(
3016 &self,
3017 tok: &CudaSlice<u32>,
3018 p: &CudaSlice<f32>,
3019 out: &mut CudaSlice<u32>,
3020 slot: usize,
3021 ) -> Result<(), Box<dyn std::error::Error>> {
3022 let f = self.func("pack_tok_p");
3023 let sl = slot as i32;
3024 let cfg = LaunchConfig {
3025 grid_dim: (1, 1, 1),
3026 block_dim: (32, 1, 1),
3027 shared_mem_bytes: 0,
3028 };
3029 let __s_b = self.gpu.stream();
3030 let mut b = __s_b.launch_builder(&f);
3031 b.arg(tok).arg(p).arg(out).arg(&sl);
3032 unsafe {
3033 b.launch(cfg)?;
3034 }
3035 Ok(())
3036 }
3037 pub fn tok_map_u32(
3038 &self,
3039 tok: &mut CudaSlice<u32>,
3040 map: &CudaSlice<u32>,
3041 ) -> Result<(), Box<dyn std::error::Error>> {
3042 let f = self.func("tok_map_u32");
3043 let cfg = LaunchConfig {
3044 grid_dim: (1, 1, 1),
3045 block_dim: (32, 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(tok).arg(map);
3051 unsafe {
3052 b.launch(cfg)?;
3053 }
3054 Ok(())
3055 }
3056
3057 #[allow(clippy::too_many_arguments)]
3059 pub fn spec_assemble_verify(
3060 &self,
3061 tokp: &CudaSlice<u32>,
3062 pend: &CudaSlice<u32>,
3063 d2t: Option<&CudaSlice<u32>>,
3064 vtok: &mut CudaSlice<u32>,
3065 brk: &mut CudaSlice<u32>,
3066 p_min: f32,
3067 k: usize,
3068 pmin0: bool,
3069 ) -> Result<(), Box<dyn std::error::Error>> {
3070 let f = self.func("spec_assemble_verify");
3071 let (ki, pm) = (k as i32, if pmin0 { 1i32 } else { 0i32 });
3072 let cfg = LaunchConfig {
3073 grid_dim: (1, 1, 1),
3074 block_dim: (32, 1, 1),
3075 shared_mem_bytes: 0,
3076 };
3077 let __s_b = self.gpu.stream();
3078 let mut b = __s_b.launch_builder(&f);
3079 match d2t {
3080 Some(m) => {
3081 b.arg(tokp)
3082 .arg(pend)
3083 .arg(m)
3084 .arg(vtok)
3085 .arg(brk)
3086 .arg(&p_min)
3087 .arg(&ki)
3088 .arg(&pm);
3089 unsafe {
3090 b.launch(cfg)?;
3091 }
3092 }
3093 None => {
3094 let null: u64 = 0;
3095 b.arg(tokp)
3096 .arg(pend)
3097 .arg(&null)
3098 .arg(vtok)
3099 .arg(brk)
3100 .arg(&p_min)
3101 .arg(&ki)
3102 .arg(&pm);
3103 unsafe {
3104 b.launch(cfg)?;
3105 }
3106 }
3107 }
3108 Ok(())
3109 }
3110
3111 #[allow(clippy::too_many_arguments)]
3113 pub fn ssm_conv_ring_rebuild_dc(
3114 &self,
3115 qkv_tm: &CudaSlice<f32>,
3116 ring_old: &CudaSlice<f32>,
3117 conv_state: &mut CudaSlice<f32>,
3118 conv_dim: usize,
3119 acc: &CudaSlice<u32>,
3120 base: usize,
3121 t_v: usize,
3122 d_conv: usize,
3123 ) -> Result<(), Box<dyn std::error::Error>> {
3124 let f = self.func("ssm_conv_ring_rebuild_f32_dc");
3125 let n = conv_dim * (d_conv - 1);
3126 let cfg = LaunchConfig::for_num_elems(n as u32);
3127 let (cd, b0, tv, dc) = (conv_dim as i32, base as i32, t_v as i32, d_conv as i32);
3128 let __s_b = self.gpu.stream();
3129 let mut b = __s_b.launch_builder(&f);
3130 b.arg(qkv_tm)
3131 .arg(ring_old)
3132 .arg(conv_state)
3133 .arg(&cd)
3134 .arg(acc)
3135 .arg(&b0)
3136 .arg(&tv)
3137 .arg(&dc);
3138 unsafe {
3139 b.launch(cfg)?;
3140 }
3141 Ok(())
3142 }
3143 #[allow(clippy::too_many_arguments)]
3144 pub fn gdn_scan_s128_dc(
3145 &self,
3146 q: &CudaSlice<f32>,
3147 k: &CudaSlice<f32>,
3148 v: &CudaSlice<f32>,
3149 g: &CudaSlice<f32>,
3150 beta: &CudaSlice<f32>,
3151 state_in: &CudaSlice<f32>,
3152 state_out: &mut CudaSlice<f32>,
3153 o: &mut CudaSlice<f32>,
3154 n_head: usize,
3155 acc: &CudaSlice<u32>,
3156 base: usize,
3157 t_v: usize,
3158 scale: f32,
3159 ) -> Result<(), Box<dyn std::error::Error>> {
3160 let f = self.func("gdn_scan_s128_dc");
3161 const S_V: u32 = 128;
3162 const WARP: u32 = 32;
3163 const COLS_PER_BLOCK: u32 = 4;
3164 let cfg = LaunchConfig {
3165 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
3166 block_dim: (WARP, COLS_PER_BLOCK, 1),
3167 shared_mem_bytes: 0,
3168 };
3169 let (h, b0, tv) = (n_head as i32, base as i32, t_v as i32);
3170 let __s_b = self.gpu.stream();
3171 let mut b = __s_b.launch_builder(&f);
3172 b.arg(q)
3173 .arg(k)
3174 .arg(v)
3175 .arg(g)
3176 .arg(beta)
3177 .arg(state_in)
3178 .arg(state_out)
3179 .arg(o)
3180 .arg(&h)
3181 .arg(acc)
3182 .arg(&b0)
3183 .arg(&tv)
3184 .arg(&scale);
3185 unsafe {
3186 b.launch(cfg)?;
3187 }
3188 Ok(())
3189 }
3190
3191 pub fn spec_rollback_kv(
3193 &self,
3194 len_ptrs: &CudaSlice<u64>,
3195 saved: &CudaSlice<i32>,
3196 acc: &CudaSlice<u32>,
3197 base: usize,
3198 n_layer: usize,
3199 ) -> Result<(), Box<dyn std::error::Error>> {
3200 let f = self.func("spec_rollback_kv");
3201 let (b, nl) = (base as i32, n_layer as i32);
3202 let cfg = LaunchConfig {
3203 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
3204 block_dim: (64, 1, 1),
3205 shared_mem_bytes: 0,
3206 };
3207 let __s_bl = self.gpu.stream();
3208 let mut bl = __s_bl.launch_builder(&f);
3209 bl.arg(len_ptrs).arg(saved).arg(acc).arg(&b).arg(&nl);
3210 unsafe {
3211 bl.launch(cfg)?;
3212 }
3213 Ok(())
3214 }
3215
3216 pub fn spec_fork_valid(
3218 &self,
3219 acc: &CudaSlice<u32>,
3220 optimistic_pending: u32,
3221 valid: &mut CudaSlice<u32>,
3222 ) -> Result<(), Box<dyn std::error::Error>> {
3223 let f = self.func("spec_fork_valid");
3224 let cfg = LaunchConfig {
3225 grid_dim: (1, 1, 1),
3226 block_dim: (1, 1, 1),
3227 shared_mem_bytes: 0,
3228 };
3229 let __s_bl = self.gpu.stream();
3230 let mut bl = __s_bl.launch_builder(&f);
3231 bl.arg(acc).arg(&optimistic_pending).arg(valid);
3232 unsafe {
3233 bl.launch(cfg)?;
3234 }
3235 Ok(())
3236 }
3237
3238 pub fn spec_fork_reconcile_kv(
3240 &self,
3241 len_ptrs: &CudaSlice<u64>,
3242 saved: &CudaSlice<i32>,
3243 acc: &CudaSlice<u32>,
3244 valid: &CudaSlice<u32>,
3245 base: usize,
3246 n_layer: usize,
3247 ) -> Result<(), Box<dyn std::error::Error>> {
3248 let f = self.func("spec_fork_reconcile_kv");
3249 let (b, nl) = (base as i32, n_layer as i32);
3250 let cfg = LaunchConfig {
3251 grid_dim: (n_layer.div_ceil(64) as u32, 1, 1),
3252 block_dim: (64, 1, 1),
3253 shared_mem_bytes: 0,
3254 };
3255 let __s_bl = self.gpu.stream();
3256 let mut bl = __s_bl.launch_builder(&f);
3257 bl.arg(len_ptrs)
3258 .arg(saved)
3259 .arg(acc)
3260 .arg(valid)
3261 .arg(&b)
3262 .arg(&nl);
3263 unsafe {
3264 bl.launch(cfg)?;
3265 }
3266 Ok(())
3267 }
3268
3269 pub fn spec_fork_restore_f32(
3271 &self,
3272 snapshot: &CudaSlice<f32>,
3273 state: &mut CudaSlice<f32>,
3274 valid: &CudaSlice<u32>,
3275 ) -> Result<(), Box<dyn std::error::Error>> {
3276 assert_eq!(
3277 snapshot.len(),
3278 state.len(),
3279 "fork recurrent snapshot shape mismatch"
3280 );
3281 let f = self.func("spec_fork_restore_f32");
3282 let n = state.len() as i32;
3283 let blocks = state.len().div_ceil(256).min(65535).max(1) as u32;
3284 let cfg = LaunchConfig {
3285 grid_dim: (blocks, 1, 1),
3286 block_dim: (256, 1, 1),
3287 shared_mem_bytes: 0,
3288 };
3289 let __s_bl = self.gpu.stream();
3290 let mut bl = __s_bl.launch_builder(&f);
3291 bl.arg(snapshot).arg(state).arg(valid).arg(&n);
3292 unsafe {
3293 bl.launch(cfg)?;
3294 }
3295 Ok(())
3296 }
3297
3298 pub fn spec_seed_gather(
3301 &self,
3302 vx: &CudaSlice<f32>,
3303 fill_prev: &CudaSlice<f32>,
3304 acc: &CudaSlice<u32>,
3305 h_seed: &mut CudaSlice<f32>,
3306 base: usize,
3307 n_embd: usize,
3308 ) -> Result<(), Box<dyn std::error::Error>> {
3309 let f = self.func("spec_seed_gather");
3310 let (b, ne) = (base as i32, n_embd as i32);
3311 let cfg = LaunchConfig {
3312 grid_dim: (n_embd.div_ceil(256) as u32, 1, 1),
3313 block_dim: (256, 1, 1),
3314 shared_mem_bytes: 0,
3315 };
3316 let __s_bl = self.gpu.stream();
3317 let mut bl = __s_bl.launch_builder(&f);
3318 bl.arg(vx)
3319 .arg(fill_prev)
3320 .arg(acc)
3321 .arg(h_seed)
3322 .arg(&b)
3323 .arg(&ne);
3324 unsafe {
3325 bl.launch(cfg)?;
3326 }
3327 Ok(())
3328 }
3329
3330 pub fn spec_accept_greedy(
3332 &self,
3333 preds: &CudaSlice<u32>,
3334 draft: &CudaSlice<u32>,
3335 last_pred: u32,
3336 base: usize,
3337 k_round: usize,
3338 out: &mut CudaSlice<u32>,
3339 ) -> Result<(), Box<dyn std::error::Error>> {
3340 let f = self.func("spec_accept_greedy");
3341 let (b, k) = (base as i32, k_round as i32);
3342 let cfg = LaunchConfig {
3343 grid_dim: (1, 1, 1),
3344 block_dim: (32, 1, 1),
3345 shared_mem_bytes: 0,
3346 };
3347 let __s_bl = self.gpu.stream();
3348 let mut bl = __s_bl.launch_builder(&f);
3349 bl.arg(preds)
3350 .arg(draft)
3351 .arg(&last_pred)
3352 .arg(&b)
3353 .arg(&k)
3354 .arg(out);
3355 unsafe {
3356 bl.launch(cfg)?;
3357 }
3358 Ok(())
3359 }
3360
3361 pub fn gumbel_perturb(
3368 &self,
3369 x: &CudaSlice<f32>,
3370 y: &mut CudaSlice<f32>,
3371 n: usize,
3372 seed: u64,
3373 stream_pos: u32,
3374 temp: f32,
3375 ) -> Result<(), Box<dyn std::error::Error>> {
3376 let f = self.func("gumbel_perturb_f32");
3377 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3378 let cfg = LaunchConfig {
3379 grid_dim: (n.div_ceil(256) as u32, 1, 1),
3380 block_dim: (256, 1, 1),
3381 shared_mem_bytes: 0,
3382 };
3383 let __s_b = self.gpu.stream();
3384 let mut b = __s_b.launch_builder(&f);
3385 b.arg(x)
3386 .arg(&mut *y)
3387 .arg(&ni)
3388 .arg(&slo)
3389 .arg(&shi)
3390 .arg(&stream_pos)
3391 .arg(&temp);
3392 unsafe {
3393 b.launch(cfg)?;
3394 }
3395 Ok(())
3396 }
3397
3398 pub fn mask_logits_col(
3406 &self,
3407 logits: &mut CudaSlice<f32>,
3408 mask: &CudaSlice<u32>,
3409 col: usize,
3410 n: usize,
3411 mask_words: usize,
3412 ) -> Result<(), Box<dyn std::error::Error>> {
3413 let f = self.func("mask_logits_f32");
3414 let (ci, ni, mw) = (col as i32, n as i32, mask_words as i32);
3415 let cfg = LaunchConfig {
3416 grid_dim: (n.div_ceil(256).min(1024) as u32, 1, 1),
3417 block_dim: (256, 1, 1),
3418 shared_mem_bytes: 0,
3419 };
3420 let __s_b = self.gpu.stream();
3421 let mut b = __s_b.launch_builder(&f);
3422 b.arg(&mut *logits).arg(mask).arg(&ci).arg(&ni).arg(&mw);
3423 unsafe {
3424 b.launch(cfg)?;
3425 }
3426 Ok(())
3427 }
3428
3429 pub fn gumbel_perturb_col(
3436 &self,
3437 x: &CudaSlice<f32>,
3438 col: usize,
3439 y: &mut CudaSlice<f32>,
3440 n: usize,
3441 seed: u64,
3442 stream_pos: u32,
3443 temp: f32,
3444 ) -> Result<(), Box<dyn std::error::Error>> {
3445 let f = self.func("gumbel_perturb_f32");
3446 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3447 let col_view = x.slice(col * n..(col + 1) * n);
3448 let cfg = LaunchConfig {
3449 grid_dim: (n.div_ceil(256) as u32, 1, 1),
3450 block_dim: (256, 1, 1),
3451 shared_mem_bytes: 0,
3452 };
3453 let __s_b = self.gpu.stream();
3454 let mut b = __s_b.launch_builder(&f);
3455 b.arg(&col_view)
3456 .arg(&mut *y)
3457 .arg(&ni)
3458 .arg(&slo)
3459 .arg(&shi)
3460 .arg(&stream_pos)
3461 .arg(&temp);
3462 unsafe {
3463 b.launch(cfg)?;
3464 }
3465 Ok(())
3466 }
3467
3468 #[allow(clippy::too_many_arguments)]
3474 pub fn gumbel_perturb_filtered_col(
3475 &self,
3476 x: &CudaSlice<f32>,
3477 col: usize,
3478 y: &mut CudaSlice<f32>,
3479 n: usize,
3480 seed: u64,
3481 stream_pos: u32,
3482 temp: f32,
3483 stat_max: &CudaSlice<f32>,
3484 stat_th: &CudaSlice<f32>,
3485 stat_idx: usize,
3486 ) -> Result<(), Box<dyn std::error::Error>> {
3487 let f = self.func("gumbel_perturb_filtered_col_f32");
3488 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3489 let (ci, si) = (col as i32, stat_idx as i32);
3490 let cfg = LaunchConfig {
3491 grid_dim: (n.div_ceil(256) as u32, 1, 1),
3492 block_dim: (256, 1, 1),
3493 shared_mem_bytes: 0,
3494 };
3495 let __s_b = self.gpu.stream();
3496 let mut b = __s_b.launch_builder(&f);
3497 b.arg(x)
3498 .arg(&ci)
3499 .arg(&mut *y)
3500 .arg(&ni)
3501 .arg(&slo)
3502 .arg(&shi)
3503 .arg(&stream_pos)
3504 .arg(&temp)
3505 .arg(stat_max)
3506 .arg(stat_th)
3507 .arg(&si);
3508 unsafe {
3509 b.launch(cfg)?;
3510 }
3511 Ok(())
3512 }
3513
3514 pub fn sctr_inc(&self, ctr: &mut CudaSlice<u32>) -> Result<(), Box<dyn std::error::Error>> {
3519 let f = self.func("memra_sctr_inc");
3520 let cfg = LaunchConfig {
3521 grid_dim: (1, 1, 1),
3522 block_dim: (1, 1, 1),
3523 shared_mem_bytes: 0,
3524 };
3525 let __s_b = self.gpu.stream();
3526 let mut b = __s_b.launch_builder(&f);
3527 b.arg(&mut *ctr);
3528 unsafe {
3529 b.launch(cfg)?;
3530 }
3531 Ok(())
3532 }
3533
3534 pub fn gumbel_perturb_ctr(
3539 &self,
3540 x: &CudaSlice<f32>,
3541 y: &mut CudaSlice<f32>,
3542 n: usize,
3543 seed: u64,
3544 ctr: &CudaSlice<u32>,
3545 temp: f32,
3546 ) -> Result<(), Box<dyn std::error::Error>> {
3547 let f = self.func("gumbel_perturb_ctr_f32");
3548 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3549 let cfg = LaunchConfig {
3550 grid_dim: (n.div_ceil(256) as u32, 1, 1),
3551 block_dim: (256, 1, 1),
3552 shared_mem_bytes: 0,
3553 };
3554 let __s_b = self.gpu.stream();
3555 let mut b = __s_b.launch_builder(&f);
3556 b.arg(x)
3557 .arg(&mut *y)
3558 .arg(&ni)
3559 .arg(&slo)
3560 .arg(&shi)
3561 .arg(ctr)
3562 .arg(&temp);
3563 unsafe {
3564 b.launch(cfg)?;
3565 }
3566 Ok(())
3567 }
3568
3569 pub fn softmax_gather(
3573 &self,
3574 x: &CudaSlice<f32>,
3575 row_stride: usize,
3576 ids: &CudaSlice<u32>,
3577 rows: &CudaSlice<i32>,
3578 out: &mut CudaSlice<f32>,
3579 n: usize,
3580 npair: usize,
3581 temp: f32,
3582 ) -> Result<(), Box<dyn std::error::Error>> {
3583 let f = self.func("softmax_gather_f32");
3584 let (ni, rs) = (n as i32, row_stride as i64);
3585 let np = npair as i32;
3586 let cfg = LaunchConfig {
3587 grid_dim: (npair as u32, 1, 1),
3588 block_dim: (256, 1, 1),
3589 shared_mem_bytes: 0,
3590 };
3591 let __s_b = self.gpu.stream();
3592 let mut b = __s_b.launch_builder(&f);
3593 b.arg(x)
3594 .arg(&rs)
3595 .arg(ids)
3596 .arg(rows)
3597 .arg(&mut *out)
3598 .arg(&ni)
3599 .arg(&np)
3600 .arg(&temp);
3601 unsafe {
3602 b.launch(cfg)?;
3603 }
3604 Ok(())
3605 }
3606
3607 pub fn residual_sample(
3611 &self,
3612 p: &CudaSlice<f32>,
3613 q: Option<&CudaSlice<f32>>,
3614 n: usize,
3615 temp: f32,
3616 seed: u64,
3617 stream_pos: u32,
3618 out_tok: &mut CudaSlice<u32>,
3619 ) -> Result<(), Box<dyn std::error::Error>> {
3620 let f = self.func("residual_sample_f32");
3621 let (ni, slo, shi) = (n as i32, (seed & 0xFFFF_FFFF) as u32, (seed >> 32) as u32);
3622 let nth = 1024u32;
3623 let cfg = LaunchConfig {
3624 grid_dim: (1, 1, 1),
3625 block_dim: (nth, 1, 1),
3626 shared_mem_bytes: 0,
3627 };
3628 let has_q: i32 = q.is_some() as i32;
3629 let qbuf = q.unwrap_or(p); let __s_b = self.gpu.stream();
3631 let mut b = __s_b.launch_builder(&f);
3632 b.arg(p)
3633 .arg(qbuf)
3634 .arg(&has_q)
3635 .arg(&ni)
3636 .arg(&temp)
3637 .arg(&slo)
3638 .arg(&shi)
3639 .arg(&stream_pos)
3640 .arg(&mut *out_tok);
3641 unsafe {
3642 b.launch(cfg)?;
3643 }
3644 Ok(())
3645 }
3646
3647 pub fn with_moe_cache<R>(
3652 &self,
3653 max_block_bytes: usize,
3654 f: impl FnOnce(
3655 &mut crate::moe_cache::MoeSlotCache,
3656 &Engine,
3657 ) -> Result<R, Box<dyn std::error::Error>>,
3658 ) -> Result<R, Box<dyn std::error::Error>> {
3659 let mut guard = self.moe_cache.lock().unwrap();
3660 if guard.is_none() {
3661 *guard = Some(crate::moe_cache::MoeSlotCache::new(self, max_block_bytes)?);
3662 }
3663 let cache = guard.as_mut().unwrap();
3664 f(cache, self)
3665 }
3666
3667 pub fn freeze_moe_cache(&self) {
3670 if let Some(cache) = self.moe_cache.lock().unwrap().as_mut() {
3671 cache.freeze();
3672 }
3673 }
3674
3675 pub fn export_moe_residency(&self) -> Option<Vec<(u16, u8, u16)>> {
3678 self.moe_cache
3679 .lock()
3680 .unwrap()
3681 .as_ref()
3682 .map(crate::moe_cache::MoeSlotCache::export_residency)
3683 }
3684
3685 pub(crate) fn moe_cache_frozen(&self) -> bool {
3686 self.moe_cache
3687 .lock()
3688 .unwrap()
3689 .as_ref()
3690 .is_some_and(crate::moe_cache::MoeSlotCache::is_frozen)
3691 }
3692
3693 pub fn frozen_cpu_experts_prefer_tokenwise_prime(&self) -> bool {
3700 crate::cpu_experts::configured()
3701 && self.moe_cache_frozen()
3702 && std::env::var("MEMRA_CPU_EXPERT_BATCHED_PRIME").as_deref() != Ok("1")
3703 }
3704
3705 pub(crate) fn configure_moe_cache_layout(&self, block_bytes: Vec<usize>) {
3707 assert!(
3708 self.moe_cache.lock().unwrap().is_none(),
3709 "MoE cache layout configured after cache construction"
3710 );
3711 *self.moe_cache_layout.lock().unwrap() = Some(block_bytes);
3712 }
3713
3714 pub(crate) fn moe_cache_layout(&self) -> Option<Vec<usize>> {
3715 self.moe_cache_layout.lock().unwrap().clone()
3716 }
3717
3718 pub fn moe_cache_enabled() -> bool {
3720 std::env::var("MEMRA_MOE_CACHE").as_deref() != Ok("0")
3721 }
3722
3723 pub fn moe_cache_stats(&self) -> Option<(u64, u64, u64, usize)> {
3726 let guard = self.moe_cache.lock().unwrap();
3727 guard
3728 .as_ref()
3729 .map(|c| (c.hits, c.misses, c.staged_bytes, c.n_slots()))
3730 }
3731
3732 pub fn cpu_expert_stats(
3736 &self,
3737 ) -> Option<(u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64)> {
3738 crate::cpu_experts::configured().then(crate::cpu_experts::stats)
3739 }
3740
3741 pub fn cpu_expert_predictor_stats(&self) -> (u64, u64) {
3744 crate::cpu_experts::predictor_stats()
3745 }
3746
3747 pub fn cpu_expert_exposed_wait_ns(&self) -> Option<u64> {
3748 crate::cpu_experts::configured().then(crate::cpu_experts::exposed_wait_ns)
3749 }
3750
3751 pub fn cpu_expert_gpu_residency_stats(&self) -> Option<(u64, u64, u64)> {
3754 crate::cpu_experts::configured().then(crate::cpu_experts::incomplete_gpu_residency_stats)
3755 }
3756
3757 pub fn moe_pread_stats(&self) -> Option<(u64, u64, u64, u64, u64, u64, u64)> {
3760 let guard = self.moe_cache.lock().unwrap();
3761 guard
3762 .as_ref()
3763 .and_then(|cache| cache.pread_stats())
3764 .map(|stats| {
3765 (
3766 stats.reads,
3767 stats.bytes,
3768 stats.read_errors,
3769 stats.short_reads,
3770 stats.fallbacks,
3771 stats.buffer_waits,
3772 stats.ring_full,
3773 )
3774 })
3775 }
3776
3777 pub fn spill_config_fallbacks(&self) -> u64 {
3779 crate::spill_pread::config_fallbacks()
3780 }
3781
3782 pub fn moe_cache_reset_counters(&self) {
3784 if let Some(c) = self.moe_cache.lock().unwrap().as_mut() {
3785 c.reset_counters();
3786 }
3787 }
3788
3789 pub fn htod_bytes(&self, v: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3790 Ok(self.gpu.stream().clone_htod(v)?)
3791 }
3792
3793 pub fn htod_bytes_padded(
3797 &self,
3798 v: &[u8],
3799 pad: usize,
3800 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
3801 let mut d = self.alloc_u8_uninit(v.len() + pad)?;
3802 {
3803 let mut view = d.slice_mut(0..v.len());
3804 self.gpu.stream().memcpy_htod(v, &mut view)?;
3805 }
3806 Ok(d)
3807 }
3808
3809 pub fn copy_into(
3811 &self,
3812 dst: &mut CudaSlice<f32>,
3813 off: usize,
3814 src: &CudaSlice<f32>,
3815 len: usize,
3816 ) -> Result<(), Box<dyn std::error::Error>> {
3817 let mut view = dst.slice_mut(off..off + len);
3818 self.gpu
3819 .stream()
3820 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3821 Ok(())
3822 }
3823
3824 pub fn copy_u8_into(
3827 &self,
3828 dst: &mut CudaSlice<u8>,
3829 off: usize,
3830 src: &CudaSlice<u8>,
3831 len: usize,
3832 ) -> Result<(), Box<dyn std::error::Error>> {
3833 let mut view = dst.slice_mut(off..off + len);
3834 self.gpu
3835 .stream()
3836 .memcpy_dtod(&src.slice(0..len), &mut view)?;
3837 Ok(())
3838 }
3839
3840 pub fn copy_u8_range_into(
3842 &self,
3843 dst: &mut CudaSlice<u8>,
3844 dst_off: usize,
3845 src: &CudaSlice<u8>,
3846 src_off: usize,
3847 len: usize,
3848 ) -> Result<(), Box<dyn std::error::Error>> {
3849 let mut dst_view = dst.slice_mut(dst_off..dst_off + len);
3850 self.gpu
3851 .stream()
3852 .memcpy_dtod(&src.slice(src_off..src_off + len), &mut dst_view)?;
3853 Ok(())
3854 }
3855
3856 pub fn prepare_kv_append(
3860 &self,
3861 kv: &mut crate::cache::KvLayer,
3862 retain_from: usize,
3863 append_rows: usize,
3864 ) -> Result<usize, Box<dyn std::error::Error>> {
3865 let Some(plan) = kv
3866 .ring
3867 .as_ref()
3868 .map(|ring| ring.append_plan(kv.len, retain_from, append_rows))
3869 .transpose()?
3870 else {
3871 return Ok(kv.len);
3872 };
3873 match plan {
3874 crate::cache::KvRingAppend::Contiguous { write_row } => Ok(write_row),
3875 crate::cache::KvRingAppend::Rebase {
3876 src_row,
3877 keep_rows,
3878 new_base,
3879 write_row,
3880 } => {
3881 if keep_rows > 0 {
3882 let k_len = keep_rows * kv.k_tok_bytes;
3883 let v_len = keep_rows * kv.v_tok_bytes;
3884 let mut k_tmp = self.alloc_u8_uninit(k_len)?;
3885 let mut v_tmp = self.alloc_u8_uninit(v_len)?;
3886 self.copy_u8_range_into(&mut k_tmp, 0, &kv.k, src_row * kv.k_tok_bytes, k_len)?;
3887 self.copy_u8_range_into(&mut v_tmp, 0, &kv.v, src_row * kv.v_tok_bytes, v_len)?;
3888 self.copy_u8_into(&mut kv.k, 0, &k_tmp, k_len)?;
3889 self.copy_u8_into(&mut kv.v, 0, &v_tmp, v_len)?;
3890 }
3891 kv.ring.as_mut().unwrap().apply_rebase(new_base);
3892 Ok(write_row)
3893 }
3894 }
3895 }
3896
3897 pub fn htod_u8_into(
3900 &self,
3901 dst: &mut CudaSlice<u8>,
3902 off: usize,
3903 src: &[u8],
3904 ) -> Result<(), Box<dyn std::error::Error>> {
3905 let mut view = dst.slice_mut(off..off + src.len());
3906 self.gpu.stream().memcpy_htod(src, &mut view)?;
3907 Ok(())
3908 }
3909
3910 pub fn view<'a>(&self, b: &'a CudaSlice<f32>, len: usize) -> cudarc::driver::CudaView<'a, f32> {
3911 b.slice(0..len)
3912 }
3913
3914 pub fn view_u8_range<'a>(
3917 &self,
3918 b: &'a CudaSlice<u8>,
3919 start: usize,
3920 end: usize,
3921 ) -> cudarc::driver::CudaView<'a, u8> {
3922 b.slice(start..end)
3923 }
3924 pub fn view_u8<'a>(
3925 &self,
3926 b: &'a CudaSlice<u8>,
3927 len: usize,
3928 ) -> cudarc::driver::CudaView<'a, u8> {
3929 b.slice(0..len)
3930 }
3931
3932 pub fn append_kv_quantized(
3936 &self,
3937 k_row: &CudaSlice<f32>,
3938 v_row: &CudaSlice<f32>,
3939 kc: &mut CudaSlice<u8>,
3940 vc: &mut CudaSlice<u8>,
3941 t: usize,
3942 kv_dim_k: usize,
3943 kv_dim_v: usize,
3944 k_tok_bytes: usize,
3945 v_tok_bytes: usize,
3946 g: bool,
3947 ) -> Result<(), Box<dyn std::error::Error>> {
3948 let f = if g {
3949 self.func_g("append_quantize_kv_q8_0_q5_1")
3950 } else {
3951 self.func("append_quantize_kv_q8_0_q5_1")
3952 };
3953 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3954 let cfg = LaunchConfig {
3955 grid_dim: (nblk, 1, 1),
3956 block_dim: (32, 1, 1),
3957 shared_mem_bytes: 0,
3958 };
3959 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
3960 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3961 let __s_b = self.gpu.stream();
3962 let mut b = __s_b.launch_builder(&f);
3963 b.arg(k_row)
3964 .arg(v_row)
3965 .arg(kc)
3966 .arg(vc)
3967 .arg(&ti)
3968 .arg(&kdk)
3969 .arg(&kdv)
3970 .arg(&ktb)
3971 .arg(&vtb);
3972 unsafe {
3973 b.launch(cfg)?;
3974 }
3975 Ok(())
3976 }
3977
3978 pub fn append_kv_quantized_dc(
3982 &self,
3983 k_row: &CudaSlice<f32>,
3984 v_row: &CudaSlice<f32>,
3985 kc: &mut CudaSlice<u8>,
3986 vc: &mut CudaSlice<u8>,
3987 t_dev: &CudaSlice<i32>,
3988 kv_dim_k: usize,
3989 kv_dim_v: usize,
3990 k_tok_bytes: usize,
3991 v_tok_bytes: usize,
3992 g: bool,
3993 ) -> Result<(), Box<dyn std::error::Error>> {
3994 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
3995 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
3996 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
3997 if Self::pdl_on() && Self::pdl_wb_on() {
3999 use cudarc::driver::{DevicePtr, DevicePtrMut};
4000 let s = &self.gpu.stream();
4001 let (pk, _g0) = k_row.device_ptr(s);
4002 let (pv, _g1) = v_row.device_ptr(s);
4003 let (pkc, _g2) = kc.device_ptr_mut(s);
4004 let (pvc, _g3) = vc.device_ptr_mut(s);
4005 let (pt, _g4) = t_dev.device_ptr(s);
4006 let mut ps = [
4007 &pk as *const _ as *mut std::ffi::c_void,
4008 &pv as *const _ as *mut _,
4009 &pkc as *const _ as *mut _,
4010 &pvc as *const _ as *mut _,
4011 &pt as *const _ as *mut _,
4012 &kdk as *const _ as *mut _,
4013 &kdv as *const _ as *mut _,
4014 &ktb as *const _ as *mut _,
4015 &vtb as *const _ as *mut _,
4016 ];
4017 unsafe {
4018 self.launch_pdl_flash(
4019 g,
4020 "append_quantize_kv_q8_0_q5_1_dc",
4021 (nblk, 1, 1),
4022 (32, 1, 1),
4023 0,
4024 &mut ps,
4025 )?;
4026 }
4027 return Ok(());
4028 }
4029 let f = if g {
4030 self.func_g("append_quantize_kv_q8_0_q5_1_dc")
4031 } else {
4032 self.func("append_quantize_kv_q8_0_q5_1_dc")
4033 };
4034 let cfg = LaunchConfig {
4035 grid_dim: (nblk, 1, 1),
4036 block_dim: (32, 1, 1),
4037 shared_mem_bytes: 0,
4038 };
4039 let __s_b = self.gpu.stream();
4040 let mut b = __s_b.launch_builder(&f);
4041 b.arg(k_row)
4042 .arg(v_row)
4043 .arg(kc)
4044 .arg(vc)
4045 .arg(t_dev)
4046 .arg(&kdk)
4047 .arg(&kdv)
4048 .arg(&ktb)
4049 .arg(&vtb);
4050 unsafe {
4051 b.launch(cfg)?;
4052 }
4053 Ok(())
4054 }
4055
4056 #[allow(clippy::too_many_arguments)]
4063 pub fn append_kv_quantized_rows(
4064 &self,
4065 k_rows: &CudaSlice<f32>,
4066 v_rows: &CudaSlice<f32>,
4067 kc: &mut CudaSlice<u8>,
4068 vc: &mut CudaSlice<u8>,
4069 t0: usize,
4070 t: usize,
4071 kv_dim_k: usize,
4072 kv_dim_v: usize,
4073 k_tok_bytes: usize,
4074 v_tok_bytes: usize,
4075 g: bool,
4076 ) -> Result<(), Box<dyn std::error::Error>> {
4077 if std::env::var("MEMRA_PRIME_APPEND_LOOP").is_ok() {
4078 for i in 0..t {
4079 let k_row = k_rows.slice(i * kv_dim_k..(i + 1) * kv_dim_k);
4080 let v_row = v_rows.slice(i * kv_dim_v..(i + 1) * kv_dim_v);
4081 self.append_kv_quantized_view(
4082 &k_row,
4083 &v_row,
4084 kc,
4085 vc,
4086 t0 + i,
4087 kv_dim_k,
4088 kv_dim_v,
4089 k_tok_bytes,
4090 v_tok_bytes,
4091 g,
4092 )?;
4093 }
4094 return Ok(());
4095 }
4096 let f = if g {
4097 self.func_g("append_quantize_kv_q8_0_q5_1_rows")
4098 } else {
4099 self.func("append_quantize_kv_q8_0_q5_1_rows")
4100 };
4101 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
4102 let cfg = LaunchConfig {
4103 grid_dim: (nblk, t as u32, 1),
4104 block_dim: (32, 1, 1),
4105 shared_mem_bytes: 0,
4106 };
4107 let (t0i, kdk, kdv) = (t0 as i32, kv_dim_k as i32, kv_dim_v as i32);
4108 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
4109 let __s_b = self.gpu.stream();
4110 let mut b = __s_b.launch_builder(&f);
4111 b.arg(k_rows)
4112 .arg(v_rows)
4113 .arg(kc)
4114 .arg(vc)
4115 .arg(&t0i)
4116 .arg(&kdk)
4117 .arg(&kdv)
4118 .arg(&ktb)
4119 .arg(&vtb);
4120 unsafe {
4121 b.launch(cfg)?;
4122 }
4123 Ok(())
4124 }
4125
4126 pub fn inc_seqlen(&self, p: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
4130 let f = self.func("inc_i32");
4131 let cfg = LaunchConfig {
4132 grid_dim: (1, 1, 1),
4133 block_dim: (1, 1, 1),
4134 shared_mem_bytes: 0,
4135 };
4136 let __s_b = self.gpu.stream();
4137 let mut b = __s_b.launch_builder(&f);
4138 b.arg(p);
4139 unsafe {
4140 b.launch(cfg)?;
4141 }
4142 Ok(())
4143 }
4144
4145 pub fn append_kv_quantized_view(
4148 &self,
4149 k_row: &cudarc::driver::CudaView<f32>,
4150 v_row: &cudarc::driver::CudaView<f32>,
4151 kc: &mut CudaSlice<u8>,
4152 vc: &mut CudaSlice<u8>,
4153 t: usize,
4154 kv_dim_k: usize,
4155 kv_dim_v: usize,
4156 k_tok_bytes: usize,
4157 v_tok_bytes: usize,
4158 g: bool,
4159 ) -> Result<(), Box<dyn std::error::Error>> {
4160 let stream = self.gpu.stream();
4161 ensure_tensor_stream_device(k_row, &stream, "append_kv_quantized_view.k_row")?;
4162 ensure_tensor_stream_device(v_row, &stream, "append_kv_quantized_view.v_row")?;
4163 ensure_tensor_stream_device(kc, &stream, "append_kv_quantized_view.k_cache")?;
4164 ensure_tensor_stream_device(vc, &stream, "append_kv_quantized_view.v_cache")?;
4165 let f = if g {
4166 self.func_g("append_quantize_kv_q8_0_q5_1")
4167 } else {
4168 self.func("append_quantize_kv_q8_0_q5_1")
4169 };
4170 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
4171 let cfg = LaunchConfig {
4172 grid_dim: (nblk, 1, 1),
4173 block_dim: (32, 1, 1),
4174 shared_mem_bytes: 0,
4175 };
4176 let (ti, kdk, kdv) = (t as i32, kv_dim_k as i32, kv_dim_v as i32);
4177 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
4178 let mut b = stream.launch_builder(&f);
4179 b.arg(k_row)
4180 .arg(v_row)
4181 .arg(kc)
4182 .arg(vc)
4183 .arg(&ti)
4184 .arg(&kdk)
4185 .arg(&kdv)
4186 .arg(&ktb)
4187 .arg(&vtb);
4188 unsafe {
4189 b.launch(cfg)?;
4190 }
4191 Ok(())
4192 }
4193
4194 pub fn copy_view_into(
4197 &self,
4198 dst: &mut CudaSlice<f32>,
4199 off: usize,
4200 src: &cudarc::driver::CudaView<f32>,
4201 len: usize,
4202 ) -> Result<(), Box<dyn std::error::Error>> {
4203 let mut view = dst.slice_mut(off..off + len);
4204 self.gpu
4205 .stream()
4206 .memcpy_dtod(&src.slice(0..len), &mut view)?;
4207 Ok(())
4208 }
4209
4210 pub fn clone_dtod(
4214 &self,
4215 src: &CudaSlice<f32>,
4216 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4217 let mut dst = self.gpu.stream().alloc_zeros::<f32>(src.len())?;
4218 self.gpu.stream().memcpy_dtod(src, &mut dst)?;
4219 Ok(dst)
4220 }
4221
4222 pub fn dtod_copy_view(
4225 &self,
4226 src: &cudarc::driver::CudaView<f32>,
4227 dst: &mut CudaSlice<f32>,
4228 ) -> Result<(), Box<dyn std::error::Error>> {
4229 self.gpu.stream().memcpy_dtod(src, dst)?;
4230 Ok(())
4231 }
4232
4233 pub fn dtod_copy_view_i8(
4235 &self,
4236 src: &cudarc::driver::CudaView<i8>,
4237 dst: &mut CudaSlice<i8>,
4238 ) -> Result<(), Box<dyn std::error::Error>> {
4239 self.gpu.stream().memcpy_dtod(src, dst)?;
4240 Ok(())
4241 }
4242
4243 pub fn dtod_copy_into(
4245 &self,
4246 src: &CudaSlice<f32>,
4247 dst: &mut CudaSlice<f32>,
4248 offset: usize,
4249 ) -> Result<(), Box<dyn std::error::Error>> {
4250 let n = src.len();
4251 let mut dv = dst.slice_mut(offset..offset + n);
4252 self.gpu.stream().memcpy_dtod(src, &mut dv)?;
4253 Ok(())
4254 }
4255
4256 pub fn copy_batch_uniform_f32(
4262 &self,
4263 table: &CudaSlice<u64>,
4264 n: usize,
4265 words: usize,
4266 ) -> Result<(), Box<dyn std::error::Error>> {
4267 if n == 0 || words == 0 {
4268 return Ok(());
4269 }
4270 debug_assert!(
4271 table.len() >= 2 * n,
4272 "pointer table must hold n srcs + n dsts"
4273 );
4274 let f = self.func("copy_batch_uniform_f32");
4275 let chunks = (words / 4).max(1).div_ceil(256).min(48) as u32;
4278 let (ni, wi) = (n as i32, words as i32);
4279 let cfg = LaunchConfig {
4280 grid_dim: (chunks, n as u32, 1),
4281 block_dim: (256, 1, 1),
4282 shared_mem_bytes: 0,
4283 };
4284 let __s = self.gpu.stream();
4285 let mut b = __s.launch_builder(&f);
4286 b.arg(table).arg(&ni).arg(&wi);
4287 unsafe {
4288 b.launch(cfg)?;
4289 }
4290 Ok(())
4291 }
4292
4293 pub fn htod_u64_into(
4296 &self,
4297 v: &[u64],
4298 dst: &mut CudaSlice<u64>,
4299 ) -> Result<(), Box<dyn std::error::Error>> {
4300 let mut view = dst.slice_mut(0..v.len());
4301 self.gpu.stream().memcpy_htod(v, &mut view)?;
4302 Ok(())
4303 }
4304
4305 pub fn copy_indirect_src_f32(
4310 &self,
4311 src_entry: &cudarc::driver::CudaView<u64>,
4312 dst: &mut CudaSlice<f32>,
4313 dst_off: usize,
4314 words: usize,
4315 ) -> Result<(), Box<dyn std::error::Error>> {
4316 let f = self.func("copy_indirect_src_f32");
4317 let chunks = (words / 4).max(1).div_ceil(256).min(48) as u32;
4318 let wi = words as i32;
4319 let cfg = LaunchConfig {
4320 grid_dim: (chunks, 1, 1),
4321 block_dim: (256, 1, 1),
4322 shared_mem_bytes: 0,
4323 };
4324 let mut dv = dst.slice_mut(dst_off..dst_off + words);
4325 let __s = self.gpu.stream();
4326 let mut b = __s.launch_builder(&f);
4327 b.arg(src_entry).arg(&mut dv).arg(&wi);
4328 unsafe {
4329 b.launch(cfg)?;
4330 }
4331 Ok(())
4332 }
4333
4334 pub fn uninit_i8(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
4336 self.alloc_uninit::<i8>(n)
4337 }
4338
4339 pub fn qmatvec(
4341 &self,
4342 w: &CudaSlice<u8>,
4343 x: &CudaSlice<f32>,
4344 m: usize,
4345 in_f: usize,
4346 out_f: usize,
4347 qtype: i32,
4348 row_bytes: usize,
4349 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4350 let f = self.func("qmatvec_f32");
4351 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
4353 grid_dim: (out_f as u32, m as u32, 1),
4354 block_dim: (256, 1, 1),
4355 shared_mem_bytes: 0,
4356 };
4357 let (inf, outf, mi, qt, rb) =
4358 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
4359 let __s_b = self.gpu.stream();
4360 let mut b = __s_b.launch_builder(&f);
4361 b.arg(w)
4362 .arg(x)
4363 .arg(&mut y)
4364 .arg(&inf)
4365 .arg(&outf)
4366 .arg(&mi)
4367 .arg(&qt)
4368 .arg(&rb);
4369 unsafe {
4370 b.launch(cfg)?;
4371 }
4372 Ok(y)
4373 }
4374
4375 pub fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
4377 let s = self.gpu.stream().alloc_zeros::<u8>(n)?;
4378 self.keep_if_capturing(&s);
4379 Ok(s)
4380 }
4381
4382 pub fn alloc_u8_uninit(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
4386 let s = unsafe { self.gpu.stream().alloc::<u8>(n)? };
4387 self.keep_if_capturing(&s);
4388 Ok(s)
4389 }
4390
4391 pub fn memset_zeros_view(
4394 &self,
4395 dst: &mut cudarc::driver::CudaViewMut<f32>,
4396 ) -> Result<(), Box<dyn std::error::Error>> {
4397 self.gpu.stream().memset_zeros(dst)?;
4398 Ok(())
4399 }
4400
4401 pub fn stage_expert(
4407 &self,
4408 host_bytes: &[u8],
4409 scratch: &mut CudaSlice<u8>,
4410 off: usize,
4411 ) -> Result<(), Box<dyn std::error::Error>> {
4412 let mut dst = scratch.slice_mut(off..off + host_bytes.len()); self.gpu.stream().memcpy_htod(host_bytes, &mut dst)?; Ok(())
4415 }
4416
4417 pub fn moe_router_topk(
4423 &self,
4424 logits: &CudaSlice<f32>,
4425 t: usize,
4426 n_expert: usize,
4427 n_used: usize,
4428 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
4429 let f = self.func("moe_router_topk_f32");
4430 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 {
4433 grid_dim: (t as u32, 1, 1),
4434 block_dim: (n_expert as u32, 1, 1),
4435 shared_mem_bytes: 0,
4436 };
4437 let (ne, nu) = (n_expert as i32, n_used as i32);
4438 let __s_b = self.gpu.stream();
4439 let mut b = __s_b.launch_builder(&f);
4440 b.arg(logits)
4441 .arg(&mut sel_idx)
4442 .arg(&mut sel_w)
4443 .arg(&ne)
4444 .arg(&nu);
4445 unsafe {
4446 b.launch(cfg)?;
4447 }
4448 Ok((sel_idx, sel_w))
4449 }
4450
4451 pub fn moe_router_topk_scaled(
4454 &self,
4455 logits: &CudaSlice<f32>,
4456 t: usize,
4457 n_expert: usize,
4458 n_used: usize,
4459 ex_scale: &CudaSlice<f32>,
4460 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
4461 let f = self.func("moe_router_topk_scaled_f32");
4466 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
4467 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
4468 let cfg = LaunchConfig {
4469 grid_dim: (t as u32, 1, 1),
4470 block_dim: (n_expert as u32, 1, 1),
4471 shared_mem_bytes: 0,
4472 };
4473 let (ne, nu) = (n_expert as i32, n_used as i32);
4474 let __s_b = self.gpu.stream();
4475 let mut b = __s_b.launch_builder(&f);
4476 b.arg(logits)
4477 .arg(&mut sel_idx)
4478 .arg(&mut sel_w)
4479 .arg(&ne)
4480 .arg(&nu)
4481 .arg(ex_scale);
4482 unsafe {
4483 b.launch(cfg)?;
4484 }
4485 Ok((sel_idx, sel_w))
4486 }
4487
4488 pub fn moe_router_topk_host(
4496 &self,
4497 logits: &CudaSlice<f32>,
4498 t: usize,
4499 n_expert: usize,
4500 n_used: usize,
4501 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
4502 let f = self.func("moe_router_topk_f32");
4503 let n = t * n_used;
4504 let mut sel_idx = self.alloc_uninit::<i32>(n)?;
4505 let mut sel_w = self.alloc_uninit::<f32>(n)?;
4506 let cfg = LaunchConfig {
4507 grid_dim: (t as u32, 1, 1),
4508 block_dim: (n_expert as u32, 1, 1),
4509 shared_mem_bytes: 0,
4510 };
4511 let (ne, nu) = (n_expert as i32, n_used as i32);
4512 let __s_b = self.gpu.stream();
4513 let mut b = __s_b.launch_builder(&f);
4514 b.arg(logits)
4515 .arg(&mut sel_idx)
4516 .arg(&mut sel_w)
4517 .arg(&ne)
4518 .arg(&nu);
4519 unsafe {
4520 b.launch(cfg)?;
4521 }
4522 let bytes = n * 8;
4524 let mut guard = self.router_stage.lock().unwrap();
4525 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
4526 *guard = Some(PinnedStage::new(bytes.max(4096))?);
4527 }
4528 let stage = guard.as_mut().unwrap();
4529 let (si, sw) = unsafe {
4530 (
4531 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
4532 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
4533 )
4534 };
4535 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()))
4539 }
4540
4541 #[allow(clippy::too_many_arguments)]
4545 pub fn moe_router_sigmoid_topk(
4546 &self,
4547 logits: &CudaSlice<f32>,
4548 t: usize,
4549 n_expert: usize,
4550 n_used: usize,
4551 active_count: usize,
4552 correction_bias: &CudaSlice<f32>,
4553 active: &CudaSlice<u8>,
4554 scaling_factor: f32,
4555 route_norm: bool,
4556 ) -> Result<(CudaSlice<i32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
4557 crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
4558 if n_expert == 0 || n_expert > 1024 || n_used == 0 || n_used > n_expert {
4559 return Err(format!(
4560 "sigmoid router shape unsupported: n_expert={n_expert}, n_used={n_used}",
4561 )
4562 .into());
4563 }
4564 if logits.len() < t * n_expert
4565 || correction_bias.len() != n_expert
4566 || active.len() != n_expert
4567 {
4568 return Err(format!(
4569 "sigmoid router buffer mismatch: logits={} bias={} active={} expected logits>={} row={}",
4570 logits.len(), correction_bias.len(), active.len(), t * n_expert, n_expert,
4571 ).into());
4572 }
4573 let f = if crate::sig_expf_dev_on() && crate::topk_fast_on() {
4574 self.func("moe_router_sigmoid_topk_f32_dexp_fast")
4577 } else if crate::sig_expf_dev_on() {
4578 self.func("moe_router_sigmoid_topk_f32_dexp")
4579 } else if crate::topk_fast_on() {
4580 self.func("moe_router_sigmoid_topk_f32_fast")
4581 } else {
4582 self.func("moe_router_sigmoid_topk_f32")
4583 };
4584 let mut sel_idx = self.alloc_uninit::<i32>(t * n_used)?;
4585 let mut sel_w = self.alloc_uninit::<f32>(t * n_used)?;
4586 let threads = n_expert.div_ceil(32) * 32;
4587 let cfg = LaunchConfig {
4588 grid_dim: (t as u32, 1, 1),
4589 block_dim: (threads as u32, 1, 1),
4590 shared_mem_bytes: 0,
4591 };
4592 let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
4593 let __s_b = self.gpu.stream();
4594 let mut b = __s_b.launch_builder(&f);
4595 b.arg(logits)
4596 .arg(correction_bias)
4597 .arg(active)
4598 .arg(&mut sel_idx)
4599 .arg(&mut sel_w)
4600 .arg(&ne)
4601 .arg(&nu)
4602 .arg(&scaling_factor)
4603 .arg(&rn);
4604 unsafe {
4605 b.launch(cfg)?;
4606 }
4607 Ok((sel_idx, sel_w))
4608 }
4609
4610 #[allow(clippy::too_many_arguments)]
4613 pub fn ring_flag_raw(&self, ptr: u64, value: u32) -> Result<(), Box<dyn std::error::Error>> {
4617 if ptr == 0 {
4618 return Err("ring_flag_raw: unarmed flag".into());
4619 }
4620 let f = self.func("memra_ring_flag");
4621 let cfg = LaunchConfig {
4622 grid_dim: (1, 1, 1),
4623 block_dim: (32, 1, 1),
4624 shared_mem_bytes: 0,
4625 };
4626 let __s_b = self.gpu.stream();
4627 let mut b = __s_b.launch_builder(&f);
4628 b.arg(&ptr).arg(&value);
4629 unsafe {
4630 b.launch(cfg)?;
4631 }
4632 Ok(())
4633 }
4634
4635 pub fn moe_sel_w_mirror(
4638 &self,
4639 sel_src: &CudaSlice<i32>,
4640 w_src: &CudaSlice<f32>,
4641 sel_dst: &mut CudaSlice<i32>,
4642 w_dst: &mut CudaSlice<f32>,
4643 n: usize,
4644 ) -> Result<(), Box<dyn std::error::Error>> {
4645 if n == 0
4646 || n > 32
4647 || sel_src.len() < n
4648 || w_src.len() < n
4649 || sel_dst.len() < n
4650 || w_dst.len() < n
4651 {
4652 return Err(format!("moe_sel_w_mirror geometry n={n}").into());
4653 }
4654 let f = self.func("moe_sel_w_mirror");
4655 let cfg = LaunchConfig {
4656 grid_dim: (1, 1, 1),
4657 block_dim: (32, 1, 1),
4658 shared_mem_bytes: 0,
4659 };
4660 let ni = n as i32;
4661 let __s_b = self.gpu.stream();
4662 let mut b = __s_b.launch_builder(&f);
4663 b.arg(sel_src).arg(w_src).arg(sel_dst).arg(w_dst).arg(&ni);
4664 unsafe {
4665 b.launch(cfg)?;
4666 }
4667 Ok(())
4668 }
4669
4670 pub fn moe_router_sigmoid_topk_into(
4671 &self,
4672 logits: &CudaSlice<f32>,
4673 t: usize,
4674 n_expert: usize,
4675 n_used: usize,
4676 active_count: usize,
4677 correction_bias: &CudaSlice<f32>,
4678 active: &CudaSlice<u8>,
4679 scaling_factor: f32,
4680 route_norm: bool,
4681 sel_idx: &mut CudaSlice<i32>,
4682 sel_w: &mut CudaSlice<f32>,
4683 ) -> Result<(), Box<dyn std::error::Error>> {
4684 crate::sigrouter_contract::validate_active_count(n_used, active_count)?;
4685 if n_expert == 0
4686 || n_expert > 1024
4687 || n_used == 0
4688 || n_used > 32 || n_used > n_expert
4690 || logits.len() < t * n_expert
4691 || correction_bias.len() != n_expert
4692 || active.len() != n_expert
4693 || sel_idx.len() < t * n_used
4694 || sel_w.len() < t * n_used
4695 {
4696 return Err("sigmoid router _into geometry mismatch".into());
4697 }
4698 let f = if crate::sig_expf_dev_on() {
4699 self.func("moe_router_sigmoid_topk_f32_dexp")
4700 } else if crate::topk_fast_on() {
4701 self.func("moe_router_sigmoid_topk_f32_fast")
4702 } else {
4703 self.func("moe_router_sigmoid_topk_f32")
4704 };
4705 let threads = n_expert.div_ceil(32) * 32;
4706 let cfg = LaunchConfig {
4707 grid_dim: (t as u32, 1, 1),
4708 block_dim: (threads as u32, 1, 1),
4709 shared_mem_bytes: 0,
4710 };
4711 let (ne, nu, rn) = (n_expert as i32, n_used as i32, i32::from(route_norm));
4712 let __s_b = self.gpu.stream();
4713 let mut b = __s_b.launch_builder(&f);
4714 b.arg(logits)
4715 .arg(correction_bias)
4716 .arg(active)
4717 .arg(&mut *sel_idx)
4718 .arg(&mut *sel_w)
4719 .arg(&ne)
4720 .arg(&nu)
4721 .arg(&scaling_factor)
4722 .arg(&rn);
4723 unsafe {
4724 b.launch(cfg)?;
4725 }
4726 Ok(())
4727 }
4728
4729 #[allow(clippy::too_many_arguments)]
4732 pub fn moe_router_sigmoid_topk_host(
4733 &self,
4734 logits: &CudaSlice<f32>,
4735 t: usize,
4736 n_expert: usize,
4737 n_used: usize,
4738 active_count: usize,
4739 correction_bias: &CudaSlice<f32>,
4740 active: &CudaSlice<u8>,
4741 scaling_factor: f32,
4742 route_norm: bool,
4743 ) -> Result<(Vec<u32>, Vec<f32>), Box<dyn std::error::Error>> {
4744 let (sel_idx, sel_w) = self.moe_router_sigmoid_topk(
4745 logits,
4746 t,
4747 n_expert,
4748 n_used,
4749 active_count,
4750 correction_bias,
4751 active,
4752 scaling_factor,
4753 route_norm,
4754 )?;
4755 let n = t * n_used;
4756 let bytes = n * 8;
4757 let mut guard = self.router_stage.lock().unwrap();
4758 if guard.as_ref().map(|p| p.cap < bytes).unwrap_or(true) {
4759 *guard = Some(PinnedStage::new(bytes.max(4096))?);
4760 }
4761 let stage = guard.as_mut().unwrap();
4762 let (si, sw) = unsafe {
4763 (
4764 std::slice::from_raw_parts_mut(stage.ptr as *mut i32, n),
4765 std::slice::from_raw_parts_mut(stage.ptr.add(n * 4) as *mut f32, n),
4766 )
4767 };
4768 self.gpu.stream().memcpy_dtoh(&sel_idx, si)?;
4769 self.gpu.stream().memcpy_dtoh(&sel_w, sw)?;
4770 self.gpu.stream().synchronize()?;
4771 Ok((si.iter().map(|&i| i as u32).collect(), sw.to_vec()))
4772 }
4773
4774 pub fn stage_expert_async(
4778 &self,
4779 host_bytes: &[u8],
4780 scratch: &mut CudaSlice<u8>,
4781 off: usize,
4782 ) -> Result<cudarc::driver::CudaEvent, Box<dyn std::error::Error>> {
4783 let mut dst = scratch.slice_mut(off..off + host_bytes.len());
4784 self.copy_stream.memcpy_htod(host_bytes, &mut dst)?;
4785 Ok(self.copy_stream.record_event(None)?)
4786 }
4787
4788 pub fn compute_wait(
4790 &self,
4791 ev: &cudarc::driver::CudaEvent,
4792 ) -> Result<(), Box<dyn std::error::Error>> {
4793 self.gpu.stream().wait(ev)?;
4794 Ok(())
4795 }
4796
4797 pub fn qmatvec_view(
4802 &self,
4803 w: &CudaSlice<u8>,
4804 range: std::ops::Range<usize>,
4805 x: &cudarc::driver::CudaView<f32>,
4806 m: usize,
4807 in_f: usize,
4808 out_f: usize,
4809 qtype: i32,
4810 row_bytes: usize,
4811 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4812 let f = self.func("qmatvec_f32");
4813 let wv = w.slice(range); let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
4816 grid_dim: (out_f as u32, m as u32, 1),
4817 block_dim: (256, 1, 1),
4818 shared_mem_bytes: 0,
4819 };
4820 let (inf, outf, mi, qt, rb) =
4821 (in_f as i32, out_f as i32, m as i32, qtype, row_bytes as i64);
4822 let __s_b = self.gpu.stream();
4823 let mut b = __s_b.launch_builder(&f);
4824 b.arg(&wv)
4825 .arg(x)
4826 .arg(&mut y)
4827 .arg(&inf)
4828 .arg(&outf)
4829 .arg(&mi)
4830 .arg(&qt)
4831 .arg(&rb);
4832 unsafe {
4833 b.launch(cfg)?;
4834 }
4835 Ok(y)
4836 }
4837
4838 #[allow(clippy::too_many_arguments)]
4845 pub fn moe_gate_up_silu8_q8(
4849 &self,
4850 gp: WPtr8,
4851 up: WPtr8,
4852 aq: &CudaSlice<i8>,
4853 ad: &CudaSlice<f32>,
4854 in_f: usize,
4855 n_ff: usize,
4856 n_used: usize,
4857 qt_g: i32,
4858 qt_u: i32,
4859 rb_g: usize,
4860 rb_u: usize,
4861 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4862 let f = self.func("moe_gate_up_silu8_q8");
4863 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
4864 let cfg = LaunchConfig {
4865 grid_dim: (n_ff as u32, n_used as u32, 1),
4866 block_dim: (32, 1, 1),
4867 shared_mem_bytes: 0,
4868 };
4869 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4870 let __s_b = self.gpu.stream();
4871 let mut b = __s_b.launch_builder(&f);
4872 b.arg(&gp)
4873 .arg(&up)
4874 .arg(aq)
4875 .arg(ad)
4876 .arg(&mut act)
4877 .arg(&inf)
4878 .arg(&nff)
4879 .arg(&qt_g)
4880 .arg(&qt_u)
4881 .arg(&rbg)
4882 .arg(&rbu);
4883 unsafe {
4884 b.launch(cfg)?;
4885 }
4886 Ok(act)
4887 }
4888
4889 #[allow(clippy::too_many_arguments)]
4890 pub fn moe_down8_fma_q8(
4891 &self,
4892 dp: WPtr8,
4893 w: F32x8,
4894 aq2: &CudaSlice<i8>,
4895 ad2: &CudaSlice<f32>,
4896 dst: &mut cudarc::driver::CudaViewMut<f32>,
4897 in_f: usize,
4898 out_f: usize,
4899 n_used: usize,
4900 qt: i32,
4901 rb: usize,
4902 ) -> Result<(), Box<dyn std::error::Error>> {
4903 let f = self.func("moe_down8_fma_q8");
4904 let cfg = LaunchConfig {
4905 grid_dim: (out_f as u32, 1, 1),
4906 block_dim: (32, 1, 1),
4907 shared_mem_bytes: 0,
4908 };
4909 let (inf, outf, nu, rbi) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
4910 let __s_b = self.gpu.stream();
4911 let mut b = __s_b.launch_builder(&f);
4912 b.arg(&dp)
4913 .arg(&w)
4914 .arg(aq2)
4915 .arg(ad2)
4916 .arg(dst)
4917 .arg(&inf)
4918 .arg(&outf)
4919 .arg(&nu)
4920 .arg(&qt)
4921 .arg(&rbi);
4922 unsafe {
4923 b.launch(cfg)?;
4924 }
4925 Ok(())
4926 }
4927
4928 pub fn qmatvec_expert_q8(
4930 &self,
4931 w: &CudaSlice<u8>,
4932 range: std::ops::Range<usize>,
4933 aq: &CudaSlice<i8>,
4934 ad: &CudaSlice<f32>,
4935 m: usize,
4936 in_f: usize,
4937 out_f: usize,
4938 qtype: i32,
4939 row_bytes: usize,
4940 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4941 let f = self.func("qmatvec_expert_q8");
4942 let wv = w.slice(range);
4943 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
4944 const ROWS: u32 = 4; let cfg = LaunchConfig {
4946 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, m as u32, 1),
4947 block_dim: (32, ROWS, 1),
4948 shared_mem_bytes: 0,
4949 };
4950 let (inf, outf, mi, rbi) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
4951 let __s_b = self.gpu.stream();
4952 let mut b = __s_b.launch_builder(&f);
4953 b.arg(&wv)
4954 .arg(aq)
4955 .arg(ad)
4956 .arg(&mut y)
4957 .arg(&inf)
4958 .arg(&outf)
4959 .arg(&mi)
4960 .arg(&qtype)
4961 .arg(&rbi);
4962 unsafe {
4963 b.launch(cfg)?;
4964 }
4965 Ok(y)
4966 }
4967
4968 pub fn moe_gate_up_silu8(
4969 &self,
4970 gp: WPtr8,
4971 up: WPtr8,
4972 x: &cudarc::driver::CudaView<f32>,
4973 in_f: usize,
4974 n_ff: usize,
4975 n_used: usize,
4976 qt_g: i32,
4977 qt_u: i32,
4978 rb_g: usize,
4979 rb_u: usize,
4980 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
4981 let f = self.func("moe_gate_up_silu8_f32");
4982 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
4984 grid_dim: (n_ff as u32, n_used as u32, 1),
4985 block_dim: (256, 1, 1),
4986 shared_mem_bytes: 0,
4987 };
4988 let (inf, nff, rbg, rbu) = (in_f as i32, n_ff as i32, rb_g as i64, rb_u as i64);
4989 let __s_b = self.gpu.stream();
4990 let mut b = __s_b.launch_builder(&f);
4991 b.arg(&gp)
4992 .arg(&up)
4993 .arg(x)
4994 .arg(&mut act)
4995 .arg(&inf)
4996 .arg(&nff)
4997 .arg(&qt_g)
4998 .arg(&qt_u)
4999 .arg(&rbg)
5000 .arg(&rbu);
5001 unsafe {
5002 b.launch(cfg)?;
5003 }
5004 Ok(act)
5005 }
5006
5007 #[allow(clippy::too_many_arguments)]
5013 pub fn moe_down8_fma_into(
5014 &self,
5015 dp: WPtr8,
5016 w: F32x8,
5017 act: &CudaSlice<f32>,
5018 dst: &mut cudarc::driver::CudaViewMut<f32>,
5019 in_f: usize,
5020 out_f: usize,
5021 n_used: usize,
5022 qt: i32,
5023 rb: usize,
5024 ) -> Result<(), Box<dyn std::error::Error>> {
5025 let f = self.func("moe_down8_fma_f32");
5026 let cfg = LaunchConfig {
5027 grid_dim: (out_f as u32, 1, 1),
5028 block_dim: (256, 1, 1),
5029 shared_mem_bytes: 0,
5030 };
5031 let (inf, outf, nu, rbv) = (in_f as i32, out_f as i32, n_used as i32, rb as i64);
5032 let __s_b = self.gpu.stream();
5033 let mut b = __s_b.launch_builder(&f);
5034 b.arg(&dp)
5035 .arg(&w)
5036 .arg(act)
5037 .arg(dst)
5038 .arg(&inf)
5039 .arg(&outf)
5040 .arg(&nu)
5041 .arg(&qt)
5042 .arg(&rbv);
5043 unsafe {
5044 b.launch(cfg)?;
5045 }
5046 Ok(())
5047 }
5048
5049 #[allow(clippy::too_many_arguments)]
5054 #[allow(clippy::too_many_arguments)]
5069 #[allow(clippy::too_many_arguments)]
5071 pub fn moe_pairs_matvec_q8(
5072 &self,
5073 table: &CudaSlice<u64>,
5074 proj: i32,
5075 pair_tok: &CudaSlice<i32>,
5076 pair_ex: &CudaSlice<i32>,
5077 aq: &CudaSlice<i8>,
5078 ad: &CudaSlice<f32>,
5079 in_f: usize,
5080 out_f: usize,
5081 n_expert: usize,
5082 n_pairs: usize,
5083 qtype: i32,
5084 row_bytes: usize,
5085 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5086 let f = self.func("moe_pairs_matvec_q8");
5087 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
5088 const ROWS: u32 = 4;
5089 let cfg = LaunchConfig {
5090 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_pairs as u32, 1),
5091 block_dim: (32, ROWS, 1),
5092 shared_mem_bytes: 0,
5093 };
5094 let (inf, outf, ne, np, rbi) = (
5095 in_f as i32,
5096 out_f as i32,
5097 n_expert as i32,
5098 n_pairs as i32,
5099 row_bytes as i64,
5100 );
5101 let __s_b = self.gpu.stream();
5102 let mut b = __s_b.launch_builder(&f);
5103 b.arg(table)
5104 .arg(&proj)
5105 .arg(pair_tok)
5106 .arg(pair_ex)
5107 .arg(aq)
5108 .arg(ad)
5109 .arg(&mut y)
5110 .arg(&inf)
5111 .arg(&outf)
5112 .arg(&ne)
5113 .arg(&np)
5114 .arg(&qtype)
5115 .arg(&rbi);
5116 unsafe {
5117 b.launch(cfg)?;
5118 }
5119 Ok(y)
5120 }
5121
5122 #[allow(clippy::too_many_arguments)]
5124 pub fn moe_pairs_matvec_q8_em(
5125 &self,
5126 table: &CudaSlice<u64>,
5127 proj: i32,
5128 ex_ids: &CudaSlice<i32>,
5129 ex_off: &CudaSlice<i32>,
5130 ex_pairs: &CudaSlice<i32>,
5131 pair_tok: &CudaSlice<i32>,
5132 aq: &CudaSlice<i8>,
5133 ad: &CudaSlice<f32>,
5134 in_f: usize,
5135 out_f: usize,
5136 n_expert: usize,
5137 n_active: usize,
5138 n_pairs: usize,
5139 qtype: i32,
5140 row_bytes: usize,
5141 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5142 let f = self.func("moe_pairs_matvec_q8_em");
5143 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
5144 const ROWS: u32 = 4;
5145 let cfg = LaunchConfig {
5146 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
5147 block_dim: (32, ROWS, 1),
5148 shared_mem_bytes: 0,
5149 };
5150 let (inf, outf, ne, na, rbi) = (
5151 in_f as i32,
5152 out_f as i32,
5153 n_expert as i32,
5154 n_active as i32,
5155 row_bytes as i64,
5156 );
5157 let __s_b = self.gpu.stream();
5158 let mut b = __s_b.launch_builder(&f);
5159 b.arg(table)
5160 .arg(&proj)
5161 .arg(ex_ids)
5162 .arg(ex_off)
5163 .arg(ex_pairs)
5164 .arg(pair_tok)
5165 .arg(aq)
5166 .arg(ad)
5167 .arg(&mut y)
5168 .arg(&inf)
5169 .arg(&outf)
5170 .arg(&ne)
5171 .arg(&na)
5172 .arg(&qtype)
5173 .arg(&rbi);
5174 unsafe {
5175 b.launch(cfg)?;
5176 }
5177 Ok(y)
5178 }
5179
5180 #[allow(clippy::too_many_arguments)]
5183 pub fn moe_pairs_matvec_q8_dec(
5184 &self,
5185 table: &CudaSlice<u64>,
5186 proj: i32,
5187 ex_ids: &CudaSlice<i32>,
5188 ex_off: &CudaSlice<i32>,
5189 ex_pairs: &CudaSlice<i32>,
5190 pair_tok: &CudaSlice<i32>,
5191 aq: &CudaSlice<i8>,
5192 ad: &CudaSlice<f32>,
5193 in_f: usize,
5194 out_f: usize,
5195 n_expert: usize,
5196 n_active: usize,
5197 n_pairs: usize,
5198 qtype: i32,
5199 row_bytes: usize,
5200 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5201 let f = self.func("moe_pairs_matvec_q8_dec");
5202 let mut y = self.alloc_uninit::<f32>(n_pairs * out_f)?;
5203 const ROWS: u32 = 4;
5204 let cfg = LaunchConfig {
5205 grid_dim: ((out_f as u32 + ROWS - 1) / ROWS, n_active as u32, 1),
5206 block_dim: (32, ROWS, 1),
5207 shared_mem_bytes: 0,
5208 };
5209 let (inf, outf, ne, na, rbi) = (
5210 in_f as i32,
5211 out_f as i32,
5212 n_expert as i32,
5213 n_active as i32,
5214 row_bytes as i64,
5215 );
5216 let __s_b = self.gpu.stream();
5217 let mut b = __s_b.launch_builder(&f);
5218 b.arg(table)
5219 .arg(&proj)
5220 .arg(ex_ids)
5221 .arg(ex_off)
5222 .arg(ex_pairs)
5223 .arg(pair_tok)
5224 .arg(aq)
5225 .arg(ad)
5226 .arg(&mut y)
5227 .arg(&inf)
5228 .arg(&outf)
5229 .arg(&ne)
5230 .arg(&na)
5231 .arg(&qtype)
5232 .arg(&rbi);
5233 unsafe {
5234 b.launch(cfg)?;
5235 }
5236 Ok(y)
5237 }
5238
5239 pub fn moe_pairs_gelu_mul(
5240 &self,
5241 gate: &CudaSlice<f32>,
5242 up: &CudaSlice<f32>,
5243 n: usize,
5244 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5245 let f = self.func("moe_pairs_gelu_mul");
5246 let mut act = self.alloc_uninit::<f32>(n)?;
5247 let cfg = LaunchConfig::for_num_elems(n as u32);
5248 let nl = n as i64;
5249 let __s_b = self.gpu.stream();
5250 let mut b = __s_b.launch_builder(&f);
5251 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
5252 unsafe {
5253 b.launch(cfg)?;
5254 }
5255 Ok(act)
5256 }
5257
5258 pub fn moe_pairs_silu_mul(
5259 &self,
5260 gate: &CudaSlice<f32>,
5261 up: &CudaSlice<f32>,
5262 n: usize,
5263 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5264 let f = self.func("moe_pairs_silu_mul");
5265 let mut act = self.alloc_uninit::<f32>(n)?;
5266 let cfg = LaunchConfig::for_num_elems(n as u32);
5267 let nl = n as i64;
5268 let __s_b = self.gpu.stream();
5269 let mut b = __s_b.launch_builder(&f);
5270 b.arg(gate).arg(up).arg(&mut act).arg(&nl);
5271 unsafe {
5272 b.launch(cfg)?;
5273 }
5274 Ok(act)
5275 }
5276
5277 #[allow(clippy::too_many_arguments)]
5278 pub fn moe_pairs_scatter(
5279 &self,
5280 y_down: &CudaSlice<f32>,
5281 pair_w: &CudaSlice<f32>,
5282 tok_pair_off: &CudaSlice<i32>,
5283 tok_pair_ids: &CudaSlice<i32>,
5284 moe_out: &mut CudaSlice<f32>,
5285 t: usize,
5286 n_embd: usize,
5287 ) -> Result<(), Box<dyn std::error::Error>> {
5288 let f = self.func("moe_pairs_scatter");
5289 let cfg = LaunchConfig {
5290 grid_dim: (((n_embd + 255) / 256) as u32, t as u32, 1),
5291 block_dim: (256, 1, 1),
5292 shared_mem_bytes: 0,
5293 };
5294 let ne = n_embd as i32;
5295 let __s_b = self.gpu.stream();
5296 let mut b = __s_b.launch_builder(&f);
5297 b.arg(y_down)
5298 .arg(pair_w)
5299 .arg(tok_pair_off)
5300 .arg(tok_pair_ids)
5301 .arg(moe_out)
5302 .arg(&ne);
5303 unsafe {
5304 b.launch(cfg)?;
5305 }
5306 Ok(())
5307 }
5308
5309 #[allow(clippy::too_many_arguments)]
5313 pub fn moe_gate_up_gelu8_dev_q8(
5314 &self,
5315 table: &CudaSlice<u64>,
5316 sel: &cudarc::driver::CudaView<i32>,
5317 aq: &CudaSlice<i8>,
5318 ad: &CudaSlice<f32>,
5319 in_f: usize,
5320 n_ff: usize,
5321 n_used: usize,
5322 n_expert: usize,
5323 qt_g: i32,
5324 qt_u: i32,
5325 rb_g: usize,
5326 rb_u: usize,
5327 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5328 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
5329 let (inf, nff, ne, rbg, rbu) = (
5330 in_f as i32,
5331 n_ff as i32,
5332 n_expert as i32,
5333 rb_g as i64,
5334 rb_u as i64,
5335 );
5336 let f = self.func("moe_gate_up_gelu8_dev_q8");
5337 let cfg = LaunchConfig {
5338 grid_dim: (n_ff as u32, n_used as u32, 1),
5339 block_dim: (32, 1, 1),
5340 shared_mem_bytes: 0,
5341 };
5342 let __s_b = self.gpu.stream();
5343 let mut b = __s_b.launch_builder(&f);
5344 b.arg(table)
5345 .arg(sel)
5346 .arg(aq)
5347 .arg(ad)
5348 .arg(&mut act)
5349 .arg(&inf)
5350 .arg(&nff)
5351 .arg(&ne)
5352 .arg(&qt_g)
5353 .arg(&qt_u)
5354 .arg(&rbg)
5355 .arg(&rbu);
5356 unsafe {
5357 b.launch(cfg)?;
5358 }
5359 Ok(act)
5360 }
5361
5362 #[allow(clippy::too_many_arguments)]
5364 pub fn moe_gate_up_gelu8_dev_q8_rows(
5365 &self,
5366 table: &CudaSlice<u64>,
5367 sel: &CudaSlice<i32>,
5368 aq: &CudaSlice<i8>,
5369 ad: &CudaSlice<f32>,
5370 t: usize,
5371 in_f: usize,
5372 n_ff: usize,
5373 n_used: usize,
5374 n_expert: usize,
5375 qt_g: i32,
5376 qt_u: i32,
5377 rb_g: usize,
5378 rb_u: usize,
5379 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5380 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
5381 let (inf, nff, ne, rbg, rbu, nu) = (
5382 in_f as i32,
5383 n_ff as i32,
5384 n_expert as i32,
5385 rb_g as i64,
5386 rb_u as i64,
5387 n_used as i32,
5388 );
5389 let f = self.func("moe_gate_up_gelu8_dev_q8_rows");
5390 let cfg = LaunchConfig {
5391 grid_dim: (n_ff as u32, n_used as u32, t as u32),
5392 block_dim: (32, 1, 1),
5393 shared_mem_bytes: 0,
5394 };
5395 let __s_b = self.gpu.stream();
5396 let mut b = __s_b.launch_builder(&f);
5397 b.arg(table)
5398 .arg(sel)
5399 .arg(aq)
5400 .arg(ad)
5401 .arg(&mut act)
5402 .arg(&inf)
5403 .arg(&nff)
5404 .arg(&ne)
5405 .arg(&qt_g)
5406 .arg(&qt_u)
5407 .arg(&rbg)
5408 .arg(&rbu)
5409 .arg(&nu);
5410 unsafe {
5411 b.launch(cfg)?;
5412 }
5413 Ok(act)
5414 }
5415
5416 #[allow(clippy::too_many_arguments)]
5418 pub fn moe_gate_up_gelu8_dev_q8_csr(
5419 &self,
5420 table: &CudaSlice<u64>,
5421 sel: &CudaSlice<i32>,
5422 aq: &CudaSlice<i8>,
5423 ad: &CudaSlice<f32>,
5424 n_pairs: usize,
5425 in_f: usize,
5426 n_ff: usize,
5427 n_used: usize,
5428 n_expert: usize,
5429 qt_g: i32,
5430 qt_u: i32,
5431 rb_g: usize,
5432 rb_u: usize,
5433 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
5434 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
5435 let (inf, nff, ne, rbg, rbu, nu, npi) = (
5436 in_f as i32,
5437 n_ff as i32,
5438 n_expert as i32,
5439 rb_g as i64,
5440 rb_u as i64,
5441 n_used as i32,
5442 n_pairs as i32,
5443 );
5444 let f = self.func("moe_gate_up_gelu8_dev_q8_csr");
5445 let cfg = LaunchConfig {
5446 grid_dim: (n_ff as u32, n_pairs as u32, 1),
5447 block_dim: (32, 1, 1),
5448 shared_mem_bytes: 0,
5449 };
5450 let __s_b = self.gpu.stream();
5451 let mut b = __s_b.launch_builder(&f);
5452 b.arg(table)
5453 .arg(sel)
5454 .arg(aq)
5455 .arg(ad)
5456 .arg(&mut act)
5457 .arg(&inf)
5458 .arg(&nff)
5459 .arg(&ne)
5460 .arg(&qt_g)
5461 .arg(&qt_u)
5462 .arg(&rbg)
5463 .arg(&rbu)
5464 .arg(&nu)
5465 .arg(&npi);
5466 unsafe {
5467 b.launch(cfg)?;
5468 }
5469 Ok(act)
5470 }
5471
5472 #[allow(clippy::too_many_arguments)]
5474 pub fn moe_down8_fma_dev_q8_rows_g(
5475 &self,
5476 table: &CudaSlice<u64>,
5477 sel: &CudaSlice<i32>,
5478 w: &CudaSlice<f32>,
5479 aq2: &CudaSlice<i8>,
5480 ad2: &CudaSlice<f32>,
5481 dst: &mut CudaSlice<f32>,
5482 t: usize,
5483 in_f: usize,
5484 out_f: usize,
5485 n_used: usize,
5486 n_expert: usize,
5487 qt: i32,
5488 rb: usize,
5489 ) -> Result<(), Box<dyn std::error::Error>> {
5490 let (inf, outf, nu, ne, rbi) = (
5491 in_f as i32,
5492 out_f as i32,
5493 n_used as i32,
5494 n_expert as i32,
5495 rb as i64,
5496 );
5497 let step_b1_w8 = t == 1 && in_f == 1280 && out_f == 4096 && n_used == 8 && qt == QT_IQ4_XS;
5501 let f = self.func(if step_b1_w8 {
5502 "moe_down8_fma_dev_q8_rows_w8"
5503 } else {
5504 "moe_down8_fma_dev_q8_rows_g"
5505 });
5506 let cfg = LaunchConfig {
5507 grid_dim: (out_f as u32, 1, t as u32),
5508 block_dim: (32, if step_b1_w8 { 8 } else { 1 }, 1),
5509 shared_mem_bytes: 0,
5510 };
5511 let __s_b = self.gpu.stream();
5512 let mut b = __s_b.launch_builder(&f);
5513 b.arg(table)
5514 .arg(sel)
5515 .arg(w)
5516 .arg(aq2)
5517 .arg(ad2)
5518 .arg(dst)
5519 .arg(&inf)
5520 .arg(&outf)
5521 .arg(&nu)
5522 .arg(&ne)
5523 .arg(&qt)
5524 .arg(&rbi);
5525 unsafe {
5526 b.launch(cfg)?;
5527 }
5528 Ok(())
5529 }
5530
5531 pub fn rp_probe_q4(&self, m: usize) -> Result<(f64, f64), Box<dyn std::error::Error>> {
5535 let (out_f, in_f) = (2048usize, 2816usize);
5536 let nblk = in_f / 32;
5537 let mut seed = 0x9E3779B97F4A7C15u64;
5538 let mut rng = move || {
5539 seed = seed
5540 .wrapping_mul(6364136223846793005)
5541 .wrapping_add(1442695040888963407);
5542 (seed >> 33) as u8
5543 };
5544 let mut w = vec![0u8; out_f * nblk * 18];
5545 for b in w.iter_mut() {
5546 *b = rng();
5547 }
5548 for r in 0..out_f {
5549 for g in 0..nblk {
5550 let off = (r * nblk + g) * 18;
5551 w[off] = 0x00;
5552 w[off + 1] = 0x2C; }
5554 }
5555 let qplane = out_f * nblk * 16;
5556 let mut wrp = vec![0u8; w.len()];
5557 for r in 0..out_f {
5558 for g in 0..nblk {
5559 let src = &w[(r * nblk + g) * 18..(r * nblk + g) * 18 + 18];
5560 wrp[qplane + (r * nblk + g) * 2..qplane + (r * nblk + g) * 2 + 2]
5561 .copy_from_slice(&src[0..2]);
5562 wrp[(r * nblk + g) * 16..(r * nblk + g) * 16 + 16].copy_from_slice(&src[2..18]);
5563 }
5564 }
5565 let w_d = self.htod_bytes(&w)?;
5566 let wrp_d = self.htod_bytes(&wrp)?;
5567 let mut aq = vec![0i8; m * in_f];
5568 for v in aq.iter_mut() {
5569 *v = rng() as i8;
5570 }
5571 let aq_d = self.htod_i8(&aq)?;
5572 let ad_d = self.htod(&vec![0.03125f32; m * nblk])?;
5573 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
5574 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
5575 const RPB: u32 = 4;
5576 let cfg = LaunchConfig {
5577 grid_dim: ((out_f as u32).div_ceil(RPB), 1, 1),
5578 block_dim: (32, RPB, 1),
5579 shared_mem_bytes: 0,
5580 };
5581 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
5582 let (rb, qp) = ((nblk * 18) as i64, qplane as i64);
5583 let fb = self.func("qmatvec_q4_0_mmvq_b4");
5584 let fr = self.func("qmatvec_q4_0_mmvq_b4_rp");
5585 {
5586 let __s_b = self.gpu.stream();
5587 let mut b = __s_b.launch_builder(&fb);
5588 b.arg(&w_d)
5589 .arg(&aq_d)
5590 .arg(&ad_d)
5591 .arg(&mut y0)
5592 .arg(&inf)
5593 .arg(&outf)
5594 .arg(&mi)
5595 .arg(&rb);
5596 unsafe {
5597 b.launch(cfg)?;
5598 }
5599 let __s_b = self.gpu.stream();
5600 let mut b = __s_b.launch_builder(&fr);
5601 b.arg(&wrp_d)
5602 .arg(&aq_d)
5603 .arg(&ad_d)
5604 .arg(&mut y1)
5605 .arg(&inf)
5606 .arg(&outf)
5607 .arg(&mi)
5608 .arg(&qp);
5609 unsafe {
5610 b.launch(cfg)?;
5611 }
5612 }
5613 self.gpu.stream().synchronize()?;
5614 let (h0, h1) = (self.dtoh(&y0)?, self.dtoh(&y1)?);
5615 let nd = h0
5616 .iter()
5617 .zip(&h1)
5618 .filter(|(a, b)| a.to_bits() != b.to_bits())
5619 .count();
5620 if nd != 0 {
5621 return Err(format!("rp twin not bitwise: {nd}/{} diffs", h0.len()).into());
5622 }
5623 let mut time = |rp: bool| -> Result<f64, Box<dyn std::error::Error>> {
5624 self.gpu.stream().synchronize()?;
5625 let t0 = std::time::Instant::now();
5626 for _ in 0..500 {
5627 if rp {
5628 let __s_b = self.gpu.stream();
5629 let mut b = __s_b.launch_builder(&fr);
5630 b.arg(&wrp_d)
5631 .arg(&aq_d)
5632 .arg(&ad_d)
5633 .arg(&mut y1)
5634 .arg(&inf)
5635 .arg(&outf)
5636 .arg(&mi)
5637 .arg(&qp);
5638 unsafe {
5639 b.launch(cfg)?;
5640 }
5641 } else {
5642 let __s_b = self.gpu.stream();
5643 let mut b = __s_b.launch_builder(&fb);
5644 b.arg(&w_d)
5645 .arg(&aq_d)
5646 .arg(&ad_d)
5647 .arg(&mut y0)
5648 .arg(&inf)
5649 .arg(&outf)
5650 .arg(&mi)
5651 .arg(&rb);
5652 unsafe {
5653 b.launch(cfg)?;
5654 }
5655 }
5656 }
5657 self.gpu.stream().synchronize()?;
5658 Ok(t0.elapsed().as_secs_f64() * 1e6 / 500.0)
5659 };
5660 let _ = time(false)?;
5661 let _ = time(true)?; Ok((time(false)?, time(true)?))
5663 }
5664
5665 pub fn build_q4_rp4(
5670 &self,
5671 t: &mut crate::model::GpuTensor,
5672 ) -> Result<(), Box<dyn std::error::Error>> {
5673 use crate::model::GpuTensor;
5674 let GpuTensor::Quant {
5675 bytes,
5676 qtype,
5677 row_bytes,
5678 ne,
5679 rp4,
5680 ..
5681 } = t
5682 else {
5683 return Ok(());
5684 };
5685 if *qtype != QT_Q4_0 || rp4.is_some() || ne.len() != 2 {
5686 return Ok(());
5687 }
5688 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5689 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 18 {
5690 return Ok(());
5691 }
5692 let nblk = in_f / 32;
5693 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 18)?;
5694 let f = self.func("q4_0_split_rp_build");
5695 let n = (out_f * nblk) as i32;
5696 let cfg = LaunchConfig {
5697 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
5698 block_dim: (256, 1, 1),
5699 shared_mem_bytes: 0,
5700 };
5701 let (of, nb) = (out_f as i32, nblk as i32);
5702 let _ = n;
5703 let __s_b = self.gpu.stream();
5704 let mut b = __s_b.launch_builder(&f);
5705 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5706 unsafe {
5707 b.launch(cfg)?;
5708 }
5709 *rp4 = Some(dst);
5710 Ok(())
5711 }
5712
5713 pub fn build_q8_rp4(
5718 &self,
5719 t: &mut crate::model::GpuTensor,
5720 ) -> Result<(), Box<dyn std::error::Error>> {
5721 use crate::model::GpuTensor;
5722 let GpuTensor::Quant {
5723 bytes,
5724 qtype,
5725 row_bytes,
5726 ne,
5727 rp4,
5728 ..
5729 } = t
5730 else {
5731 return Ok(());
5732 };
5733 if *qtype != QT_Q8_0 || rp4.is_some() || ne.len() != 2 {
5734 return Ok(());
5735 }
5736 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5737 if in_f % 32 != 0 || *row_bytes != (in_f / 32) * 34 {
5738 return Ok(());
5739 }
5740 *rp4 = Some(self.build_q8_rp4_raw(bytes, in_f, out_f)?);
5741 Ok(())
5742 }
5743
5744 pub fn build_q8_rp4_raw(
5747 &self,
5748 bytes: &CudaSlice<u8>,
5749 in_f: usize,
5750 out_f: usize,
5751 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5752 assert!(in_f % 32 == 0);
5753 let nblk = in_f / 32;
5754 let mut dst = self.alloc_uninit::<u8>(out_f * nblk * 34)?;
5755 let f = self.func("q8_0_split_rp_build");
5756 let cfg = LaunchConfig {
5757 grid_dim: (((out_f * nblk) as u32).div_ceil(256), 1, 1),
5758 block_dim: (256, 1, 1),
5759 shared_mem_bytes: 0,
5760 };
5761 let (of, nb) = (out_f as i32, nblk as i32);
5762 let __s_b = self.gpu.stream();
5763 let mut b = __s_b.launch_builder(&f);
5764 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5765 unsafe {
5766 b.launch(cfg)?;
5767 }
5768 Ok(dst)
5769 }
5770
5771 pub fn build_q4k_rp4(
5779 &self,
5780 t: &mut crate::model::GpuTensor,
5781 ) -> Result<(), Box<dyn std::error::Error>> {
5782 use crate::model::GpuTensor;
5783 let GpuTensor::Quant {
5784 bytes,
5785 qtype,
5786 row_bytes,
5787 ne,
5788 rp4,
5789 ..
5790 } = t
5791 else {
5792 return Ok(());
5793 };
5794 if *qtype != QT_Q4_K || rp4.is_some() || ne.len() != 2 {
5795 return Ok(());
5796 }
5797 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5798 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 144 {
5799 return Ok(());
5800 }
5801 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q4_K)?);
5802 Ok(())
5803 }
5804
5805 pub fn build_q6k_rp4(
5806 &self,
5807 t: &mut crate::model::GpuTensor,
5808 ) -> Result<(), Box<dyn std::error::Error>> {
5809 use crate::model::GpuTensor;
5810 let GpuTensor::Quant {
5811 bytes,
5812 qtype,
5813 row_bytes,
5814 ne,
5815 rp4,
5816 ..
5817 } = t
5818 else {
5819 return Ok(());
5820 };
5821 if *qtype != QT_Q6_K || rp4.is_some() || ne.len() != 2 {
5822 return Ok(());
5823 }
5824 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
5825 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * 210 {
5826 return Ok(());
5827 }
5828 *rp4 = Some(self.build_kq_rp4_raw(bytes, in_f, out_f, QT_Q6_K)?);
5829 Ok(())
5830 }
5831
5832 pub fn build_kq_rp4_raw(
5834 &self,
5835 bytes: &CudaSlice<u8>,
5836 in_f: usize,
5837 out_f: usize,
5838 qtype: i32,
5839 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
5840 assert!(in_f % 256 == 0);
5841 let nsbk = in_f / 256;
5842 let (sb_bytes, kname) = match qtype {
5843 QT_Q4_K => (144usize, "q4_K_split_rp_build"),
5844 QT_Q6_K => (210usize, "q6_K_split_rp_build"),
5845 _ => return Err(format!("build_kq_rp4_raw: qtype {qtype} has no rp mirror").into()),
5846 };
5847 let mut dst = self.alloc_uninit::<u8>(out_f * nsbk * sb_bytes)?;
5848 let f = self.func(kname);
5849 let cfg = LaunchConfig {
5850 grid_dim: (((out_f * nsbk) as u32).div_ceil(256), 1, 1),
5851 block_dim: (256, 1, 1),
5852 shared_mem_bytes: 0,
5853 };
5854 let (of, nb) = (out_f as i32, nsbk as i32);
5855 let __s_b = self.gpu.stream();
5856 let mut b = __s_b.launch_builder(&f);
5857 b.arg(&*bytes).arg(&mut dst).arg(&of).arg(&nb);
5858 unsafe {
5859 b.launch(cfg)?;
5860 }
5861 Ok(dst)
5862 }
5863
5864 pub fn kqrp_enabled() -> bool {
5868 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5869 *ON.get_or_init(|| match std::env::var("MEMRA_KQRP").as_deref() {
5870 Ok("0") => false,
5871 Ok(_) => true,
5872 Err(_) => cfg!(memra_hopper_mma),
5873 })
5874 }
5875
5876 pub fn build_q4_rp_swap(
5882 &self,
5883 t: &mut crate::model::GpuTensor,
5884 ) -> Result<bool, Box<dyn std::error::Error>> {
5885 use crate::model::GpuTensor;
5886 if !matches!(t, GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0) {
5896 return Ok(false);
5897 }
5898 self.build_q4_rp4(t)?;
5899 self.gpu.stream().synchronize()?; let GpuTensor::Quant { bytes, rp4, rp, .. } = t else {
5901 return Ok(false);
5902 };
5903 match rp4.take() {
5904 Some(split) => {
5905 *bytes = split; *rp = true;
5907 Ok(true)
5908 }
5909 None => Ok(false),
5910 }
5911 }
5912
5913 pub fn q4rp_enabled() -> bool {
5915 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
5916 *ON.get_or_init(|| {
5917 std::env::var("MEMRA_Q4RP")
5918 .map(|v| v != "0")
5919 .unwrap_or(true)
5920 })
5921 }
5922
5923 pub fn copy_rows_strided(
5926 &self,
5927 src: &CudaSlice<f32>,
5928 dst: &mut CudaSlice<f32>,
5929 row_elems: usize,
5930 n_rows: usize,
5931 src_stride: usize,
5932 src_off: usize,
5933 ) -> Result<(), Box<dyn std::error::Error>> {
5934 let f = self.func("copy_rows_strided_f32");
5935 let cfg = LaunchConfig {
5936 grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
5937 block_dim: (256, 1, 1),
5938 shared_mem_bytes: 0,
5939 };
5940 let (re, nr) = (row_elems as i32, n_rows as i32);
5941 let (st, off) = (src_stride as i64, src_off as i64);
5942 let __s_b = self.gpu.stream();
5943 let mut b = __s_b.launch_builder(&f);
5944 b.arg(src)
5945 .arg(&mut *dst)
5946 .arg(&re)
5947 .arg(&nr)
5948 .arg(&st)
5949 .arg(&off);
5950 unsafe {
5951 b.launch(cfg)?;
5952 }
5953 Ok(())
5954 }
5955
5956 pub fn place_rows_strided(
5962 &self,
5963 src: &CudaSlice<f32>,
5964 dst: &mut CudaSlice<f32>,
5965 row_elems: usize,
5966 n_rows: usize,
5967 dst_stride: usize,
5968 dst_off: usize,
5969 ) -> Result<(), Box<dyn std::error::Error>> {
5970 if row_elems == 0 || n_rows == 0 {
5971 return Err("strided row placement requires nonzero rows and row width".into());
5972 }
5973 let src_len = n_rows
5974 .checked_mul(row_elems)
5975 .ok_or("strided row placement source size overflow")?;
5976 let dst_len = n_rows
5977 .checked_sub(1)
5978 .and_then(|rows| rows.checked_mul(dst_stride))
5979 .and_then(|base| base.checked_add(dst_off))
5980 .and_then(|base| base.checked_add(row_elems))
5981 .ok_or("strided row placement destination size overflow")?;
5982 let row_end = dst_off
5983 .checked_add(row_elems)
5984 .ok_or("strided row placement row size overflow")?;
5985 if src.len() < src_len || dst.len() < dst_len || row_end > dst_stride {
5986 return Err(format!(
5987 "strided row placement geometry mismatch: src={} need_src={src_len} \
5988 dst={} need_dst={dst_len} row_elems={row_elems} rows={n_rows} \
5989 dst_stride={dst_stride} dst_off={dst_off}",
5990 src.len(),
5991 dst.len(),
5992 )
5993 .into());
5994 }
5995 if row_elems > i32::MAX as usize || n_rows > i32::MAX as usize {
5996 return Err("strided row placement exceeds CUDA kernel geometry".into());
5997 }
5998 let f = self.func("place_rows_strided_f32");
5999 let cfg = LaunchConfig {
6000 grid_dim: (((row_elems as u32 + 255) / 256).max(1), n_rows as u32, 1),
6001 block_dim: (256, 1, 1),
6002 shared_mem_bytes: 0,
6003 };
6004 let (re, nr) = (row_elems as i32, n_rows as i32);
6005 let (st, off) = (dst_stride as i64, dst_off as i64);
6006 let __s_b = self.gpu.stream();
6007 let mut b = __s_b.launch_builder(&f);
6008 b.arg(src)
6009 .arg(&mut *dst)
6010 .arg(&re)
6011 .arg(&nr)
6012 .arg(&st)
6013 .arg(&off);
6014 unsafe {
6015 b.launch(cfg)?;
6016 }
6017 Ok(())
6018 }
6019
6020 pub fn u32_set_k(
6022 &self,
6023 dst: &mut CudaSlice<u32>,
6024 v: u32,
6025 idx: usize,
6026 ) -> Result<(), Box<dyn std::error::Error>> {
6027 let f = self.func("u32_set_k");
6028 let cfg = LaunchConfig {
6029 grid_dim: (1, 1, 1),
6030 block_dim: (1, 1, 1),
6031 shared_mem_bytes: 0,
6032 };
6033 let ii = idx as i32;
6034 let __s_b = self.gpu.stream();
6035 let mut b = __s_b.launch_builder(&f);
6036 b.arg(dst).arg(&v).arg(&ii);
6037 unsafe {
6038 b.launch(cfg)?;
6039 }
6040 Ok(())
6041 }
6042
6043 pub fn i32_add_k(
6045 &self,
6046 d: &mut CudaSlice<i32>,
6047 v: i32,
6048 ) -> Result<(), Box<dyn std::error::Error>> {
6049 let f = self.func("i32_add_k");
6050 let cfg = LaunchConfig {
6051 grid_dim: (1, 1, 1),
6052 block_dim: (32, 1, 1),
6053 shared_mem_bytes: 0,
6054 };
6055 let __s_b = self.gpu.stream();
6056 let mut b = __s_b.launch_builder(&f);
6057 b.arg(d).arg(&v);
6058 unsafe {
6059 b.launch(cfg)?;
6060 }
6061 Ok(())
6062 }
6063
6064 pub fn i32_iota_from(
6066 &self,
6067 ctr: &CudaSlice<i32>,
6068 dst: &mut CudaSlice<i32>,
6069 n: usize,
6070 ) -> Result<(), Box<dyn std::error::Error>> {
6071 let f = self.func("i32_iota_from");
6072 let cfg = LaunchConfig::for_num_elems(n as u32);
6073 let ni = n as i32;
6074 let __s_b = self.gpu.stream();
6075 let mut b = __s_b.launch_builder(&f);
6076 b.arg(ctr).arg(dst).arg(&ni);
6077 unsafe {
6078 b.launch(cfg)?;
6079 }
6080 Ok(())
6081 }
6082
6083 pub fn u32_map_k(
6085 &self,
6086 buf: &mut CudaSlice<u32>,
6087 map: &CudaSlice<u32>,
6088 idx: usize,
6089 ) -> Result<(), Box<dyn std::error::Error>> {
6090 let f = self.func("u32_map_k");
6091 let cfg = LaunchConfig {
6092 grid_dim: (1, 1, 1),
6093 block_dim: (1, 1, 1),
6094 shared_mem_bytes: 0,
6095 };
6096 let ii = idx as i32;
6097 let __s_b = self.gpu.stream();
6098 let mut b = __s_b.launch_builder(&f);
6099 b.arg(buf).arg(map).arg(&ii);
6100 unsafe {
6101 b.launch(cfg)?;
6102 }
6103 Ok(())
6104 }
6105
6106 #[allow(clippy::too_many_arguments)]
6108 pub fn u32_pack2(
6109 &self,
6110 a: &CudaSlice<u32>,
6111 off_a: usize,
6112 n1: usize,
6113 b_in: &CudaSlice<u32>,
6114 n2: usize,
6115 out: &mut CudaSlice<u32>,
6116 ) -> Result<(), Box<dyn std::error::Error>> {
6117 let f = self.func("u32_pack2");
6118 let cfg = LaunchConfig::for_num_elems((n1 + n2) as u32);
6119 let (oa, i1, i2) = (off_a as i32, n1 as i32, n2 as i32);
6120 let __s_b = self.gpu.stream();
6121 let mut b = __s_b.launch_builder(&f);
6122 b.arg(a).arg(&oa).arg(&i1).arg(b_in).arg(&i2).arg(out);
6123 unsafe {
6124 b.launch(cfg)?;
6125 }
6126 Ok(())
6127 }
6128
6129 pub fn moe_w_exscale(
6131 &self,
6132 w: &mut CudaSlice<f32>,
6133 sel: &CudaSlice<i32>,
6134 s: &CudaSlice<f32>,
6135 n: usize,
6136 ) -> Result<(), Box<dyn std::error::Error>> {
6137 let f = self.func("moe_w_exscale");
6138 let cfg = LaunchConfig::for_num_elems(n as u32);
6139 let ni = n as i32;
6140 let __s_b = self.gpu.stream();
6141 let mut b = __s_b.launch_builder(&f);
6142 b.arg(w).arg(sel).arg(s).arg(&ni);
6143 unsafe {
6144 b.launch(cfg)?;
6145 }
6146 Ok(())
6147 }
6148
6149 pub fn moe_w_scale_by_expert(
6152 &self,
6153 w: &mut CudaSlice<f32>,
6154 sel: &CudaSlice<i32>,
6155 macros: &CudaSlice<f32>,
6156 n_expert: usize,
6157 n: usize,
6158 ) -> Result<(), Box<dyn std::error::Error>> {
6159 let f = self.func("moe_w_scale_by_expert");
6160 let cfg = LaunchConfig {
6161 grid_dim: (n.div_ceil(64) as u32, 1, 1),
6162 block_dim: (64, 1, 1),
6163 shared_mem_bytes: 0,
6164 };
6165 let (ne, nn) = (n_expert as i32, n as i32);
6166 let __s_b = self.gpu.stream();
6167 let mut b = __s_b.launch_builder(&f);
6168 b.arg(w).arg(sel).arg(macros).arg(&ne).arg(&nn);
6169 unsafe {
6170 b.launch(cfg)?;
6171 }
6172 Ok(())
6173 }
6174
6175 pub fn moe_gate_up_silu8_dev_q8(
6176 &self,
6177 table: &CudaSlice<u64>,
6178 sel: &cudarc::driver::CudaView<i32>,
6179 aq: &CudaSlice<i8>,
6180 ad: &CudaSlice<f32>,
6181 in_f: usize,
6182 n_ff: usize,
6183 n_used: usize,
6184 n_expert: usize,
6185 qt_g: i32,
6186 qt_u: i32,
6187 rb_g: usize,
6188 rb_u: usize,
6189 macros: &CudaSlice<f32>,
6190 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6191 static GU: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
6192 let (mode, wpb) = GU.get_or_init(|| {
6193 let mode = std::env::var("MEMRA_MOE_DEVQ8_GU").unwrap_or_default();
6194 let wpb = std::env::var("MEMRA_MOE_DEVQ8_WPB")
6195 .ok()
6196 .and_then(|v| v.parse().ok())
6197 .unwrap_or(4u32)
6198 .clamp(1, 16);
6199 (mode, wpb)
6200 });
6201 let (mode, wpb) = (mode.as_str(), *wpb);
6202 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
6203 let (inf, nff, ne, rbg, rbu) = (
6204 in_f as i32,
6205 n_ff as i32,
6206 n_expert as i32,
6207 rb_g as i64,
6208 rb_u as i64,
6209 );
6210 let (f, cfg) = match mode {
6211 "1" | "2" | "4" => {
6212 let rpw: u32 = mode.parse().unwrap();
6213 let f = self.func(match rpw {
6214 1 => "moe_gate_up_silu8_dev_q8_r1",
6215 2 => "moe_gate_up_silu8_dev_q8_r2",
6216 _ => "moe_gate_up_silu8_dev_q8_r4",
6217 });
6218 let rows_per_block = (rpw * wpb) as usize;
6219 let gx = n_ff.div_ceil(rows_per_block) as u32;
6220 (
6221 f,
6222 LaunchConfig {
6223 grid_dim: (gx, n_used as u32, 1),
6224 block_dim: (32, wpb, 1),
6225 shared_mem_bytes: 0,
6226 },
6227 )
6228 }
6229 "j8" if n_used <= 32 => (
6230 self.func("moe_gate_up_silu8_dev_q8_j8"),
6231 LaunchConfig {
6232 grid_dim: (n_ff as u32, 1, 1),
6233 block_dim: (32, n_used as u32, 1),
6234 shared_mem_bytes: 0,
6235 },
6236 ),
6237 "vsm2" => {
6239 let f = self.func("moe_gate_up_silu8_dev_q8_vsm2");
6240 let sh = (rb_g + rb_u) as u32;
6241 use cudarc::driver::sys::CUfunction_attribute_enum as A;
6242 f.set_attribute(
6243 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
6244 sh as i32,
6245 )?;
6246 (
6247 f,
6248 LaunchConfig {
6249 grid_dim: (n_ff as u32, n_used as u32, 1),
6250 block_dim: (32, 1, 1),
6251 shared_mem_bytes: sh,
6252 },
6253 )
6254 }
6255 "vsm" => {
6256 let f = self.func("moe_gate_up_silu8_dev_q8_vsm");
6257 let sh = (rb_g + rb_u) as u32;
6258 use cudarc::driver::sys::CUfunction_attribute_enum as A;
6259 f.set_attribute(
6260 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
6261 sh as i32,
6262 )?;
6263 (
6264 f,
6265 LaunchConfig {
6266 grid_dim: (n_ff as u32, n_used as u32, 1),
6267 block_dim: (32, 1, 1),
6268 shared_mem_bytes: sh,
6269 },
6270 )
6271 }
6272 "sg" => (
6273 self.func("moe_gate_up_silu8_dev_q8_sg"),
6274 LaunchConfig {
6275 grid_dim: (n_ff as u32, n_used as u32, 1),
6276 block_dim: (32, 1, 1),
6277 shared_mem_bytes: 0,
6278 },
6279 ),
6280 "j8sg" if n_used <= 32 => (
6281 self.func("moe_gate_up_silu8_dev_q8_j8sg"),
6282 LaunchConfig {
6283 grid_dim: (n_ff as u32, 1, 1),
6284 block_dim: (32, n_used as u32, 1),
6285 shared_mem_bytes: 0,
6286 },
6287 ),
6288 "u64" if in_f == 2048 => (
6289 self.func("moe_gate_up_silu8_dev_q8_u64"),
6290 LaunchConfig {
6291 grid_dim: (n_ff as u32, n_used as u32, 1),
6292 block_dim: (32, 1, 1),
6293 shared_mem_bytes: 0,
6294 },
6295 ),
6296 "gs4" if in_f == 2048 => (
6297 self.func("moe_gate_up_silu8_dev_q8_gs4"),
6298 LaunchConfig {
6299 grid_dim: (n_ff as u32, n_used as u32, 1),
6300 block_dim: (32, 4, 1),
6301 shared_mem_bytes: 0,
6302 },
6303 ),
6304 "v" | "" => (
6306 self.func("moe_gate_up_silu8_dev_q8_v"),
6307 LaunchConfig {
6308 grid_dim: (n_ff as u32, n_used as u32, 1),
6309 block_dim: (32, 1, 1),
6310 shared_mem_bytes: 0,
6311 },
6312 ),
6313 "s2" => (
6314 self.func("moe_gate_up_silu8_dev_q8_s2"),
6315 LaunchConfig {
6316 grid_dim: (n_ff as u32, n_used as u32, 1),
6317 block_dim: (32, 2, 1),
6318 shared_mem_bytes: 0,
6319 },
6320 ),
6321 "s2z" => {
6322 let rz = wpb.min(16); (
6324 self.func("moe_gate_up_silu8_dev_q8_s2z"),
6325 LaunchConfig {
6326 grid_dim: (n_ff.div_ceil(rz as usize) as u32, n_used as u32, 1),
6327 block_dim: (32, 2, rz),
6328 shared_mem_bytes: 0,
6329 },
6330 )
6331 }
6332 _ => (
6333 self.func("moe_gate_up_silu8_dev_q8"),
6334 LaunchConfig {
6335 grid_dim: (n_ff as u32, n_used as u32, 1),
6336 block_dim: (32, 1, 1),
6337 shared_mem_bytes: 0,
6338 },
6339 ),
6340 };
6341 let __s_b = self.gpu.stream();
6342 let mut b = __s_b.launch_builder(&f);
6343 b.arg(table)
6344 .arg(sel)
6345 .arg(aq)
6346 .arg(ad)
6347 .arg(&mut act)
6348 .arg(&inf)
6349 .arg(&nff)
6350 .arg(&ne)
6351 .arg(&qt_g)
6352 .arg(&qt_u)
6353 .arg(&rbg)
6354 .arg(&rbu)
6355 .arg(macros);
6356 unsafe {
6357 b.launch(cfg)?;
6358 }
6359 Ok(act)
6360 }
6361
6362 #[allow(clippy::too_many_arguments)]
6363 pub fn moe_down8_fma_dev_q8(
6364 &self,
6365 table: &CudaSlice<u64>,
6366 sel: &cudarc::driver::CudaView<i32>,
6367 w: &cudarc::driver::CudaView<f32>,
6368 aq2: &CudaSlice<i8>,
6369 ad2: &CudaSlice<f32>,
6370 dst: &mut cudarc::driver::CudaViewMut<f32>,
6371 in_f: usize,
6372 out_f: usize,
6373 n_used: usize,
6374 n_expert: usize,
6375 qt: i32,
6376 rb: usize,
6377 ) -> Result<(), Box<dyn std::error::Error>> {
6378 static DOWN: std::sync::OnceLock<String> = std::sync::OnceLock::new();
6379 let mode = DOWN.get_or_init(|| std::env::var("MEMRA_MOE_DEVQ8_DOWN").unwrap_or_default());
6380 let (inf, outf, nu, ne, rbi) = (
6381 in_f as i32,
6382 out_f as i32,
6383 n_used as i32,
6384 n_expert as i32,
6385 rb as i64,
6386 );
6387 let (f, cfg) = match mode.as_str() {
6390 m @ ("1" | "2" | "4") if n_used <= 8 => {
6391 let rpw: usize = m.parse().unwrap();
6392 let f = self.func(match rpw {
6393 1 => "moe_down8_fma_dev_q8_w8r1",
6394 2 => "moe_down8_fma_dev_q8_w8r2",
6395 _ => "moe_down8_fma_dev_q8_w8r4",
6396 });
6397 (
6398 f,
6399 LaunchConfig {
6400 grid_dim: (out_f.div_ceil(rpw) as u32, 1, 1),
6401 block_dim: (32, n_used as u32, 1),
6402 shared_mem_bytes: 0,
6403 },
6404 )
6405 }
6406 "h2" if in_f == 512 => (
6407 self.func("moe_down8_fma_dev_q8_h2"),
6408 LaunchConfig {
6409 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
6410 block_dim: (32, 1, 1),
6411 shared_mem_bytes: 0,
6412 },
6413 ),
6414 "" if in_f == 704 && n_used <= 8 => (
6417 self.func("moe_down8_fma_dev_q8_w8r2"),
6418 LaunchConfig {
6419 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
6420 block_dim: (32, n_used as u32, 1),
6421 shared_mem_bytes: 0,
6422 },
6423 ),
6424 "w8h2v" | "" if in_f == 512 && n_used <= 8 => (
6428 self.func("moe_down8_fma_dev_q8_w8h2v"),
6429 LaunchConfig {
6430 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
6431 block_dim: (32, n_used as u32, 1),
6432 shared_mem_bytes: 0,
6433 },
6434 ),
6435 "w8h2r2v" if in_f == 512 && n_used <= 8 => (
6436 self.func("moe_down8_fma_dev_q8_w8h2r2v"),
6437 LaunchConfig {
6438 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
6439 block_dim: (32, n_used as u32, 1),
6440 shared_mem_bytes: 0,
6441 },
6442 ),
6443 "w8h2r2" if in_f == 512 && n_used <= 8 => (
6444 self.func("moe_down8_fma_dev_q8_w8h2r2"),
6445 LaunchConfig {
6446 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
6447 block_dim: (32, n_used as u32, 1),
6448 shared_mem_bytes: 0,
6449 },
6450 ),
6451 "w8h2" if in_f == 512 && n_used <= 8 => (
6452 self.func("moe_down8_fma_dev_q8_w8h2"),
6453 LaunchConfig {
6454 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
6455 block_dim: (32, n_used as u32, 1),
6456 shared_mem_bytes: 0,
6457 },
6458 ),
6459 _ => (
6460 self.func("moe_down8_fma_dev_q8"),
6461 LaunchConfig {
6462 grid_dim: (out_f as u32, 1, 1),
6463 block_dim: (32, 1, 1),
6464 shared_mem_bytes: 0,
6465 },
6466 ),
6467 };
6468 let __s_b = self.gpu.stream();
6469 let mut b = __s_b.launch_builder(&f);
6470 b.arg(table)
6471 .arg(sel)
6472 .arg(w)
6473 .arg(aq2)
6474 .arg(ad2)
6475 .arg(dst)
6476 .arg(&inf)
6477 .arg(&outf)
6478 .arg(&nu)
6479 .arg(&ne)
6480 .arg(&qt)
6481 .arg(&rbi);
6482 unsafe {
6483 b.launch(cfg)?;
6484 }
6485 Ok(())
6486 }
6487
6488 #[allow(clippy::too_many_arguments)]
6495 pub fn moe_gate_up_silu8_dev_q8_rows(
6496 &self,
6497 table: &CudaSlice<u64>,
6498 sel: &CudaSlice<i32>,
6499 aq: &CudaSlice<i8>,
6500 ad: &CudaSlice<f32>,
6501 t: usize,
6502 in_f: usize,
6503 n_ff: usize,
6504 n_used: usize,
6505 n_expert: usize,
6506 qt_g: i32,
6507 qt_u: i32,
6508 rb_g: usize,
6509 rb_u: usize,
6510 macros: &CudaSlice<f32>,
6511 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6512 let f = self.func("moe_gate_up_silu8_dev_q8_v_rows");
6513 let mut act = self.alloc_uninit::<f32>(t * n_used * n_ff)?;
6514 let cfg = LaunchConfig {
6515 grid_dim: (n_ff as u32, n_used as u32, t as u32),
6516 block_dim: (32, 1, 1),
6517 shared_mem_bytes: 0,
6518 };
6519 let (inf, nff, ne, nu, rbg, rbu) = (
6520 in_f as i32,
6521 n_ff as i32,
6522 n_expert as i32,
6523 n_used as i32,
6524 rb_g as i64,
6525 rb_u as i64,
6526 );
6527 let __s_b = self.gpu.stream();
6528 let mut b = __s_b.launch_builder(&f);
6529 b.arg(table)
6530 .arg(sel)
6531 .arg(aq)
6532 .arg(ad)
6533 .arg(&mut act)
6534 .arg(&inf)
6535 .arg(&nff)
6536 .arg(&ne)
6537 .arg(&qt_g)
6538 .arg(&qt_u)
6539 .arg(&rbg)
6540 .arg(&rbu)
6541 .arg(&nu)
6542 .arg(macros);
6543 unsafe {
6544 b.launch(cfg)?;
6545 }
6546 Ok(act)
6547 }
6548
6549 #[allow(clippy::too_many_arguments)]
6554 pub fn moe_down8_fma_dev_q8_rows(
6555 &self,
6556 table: &CudaSlice<u64>,
6557 sel: &CudaSlice<i32>,
6558 w: &CudaSlice<f32>,
6559 aq2: &CudaSlice<i8>,
6560 ad2: &CudaSlice<f32>,
6561 dst: &mut CudaSlice<f32>,
6562 t: usize,
6563 in_f: usize,
6564 out_f: usize,
6565 n_used: usize,
6566 n_expert: usize,
6567 qt: i32,
6568 rb: usize,
6569 ) -> Result<(), Box<dyn std::error::Error>> {
6570 assert!(
6571 in_f == 512 && n_used <= 8,
6572 "down rows twin is w8h2v shape-gated"
6573 );
6574 let f = self.func("moe_down8_fma_dev_q8_w8h2v_rows");
6575 let cfg = LaunchConfig {
6576 grid_dim: (out_f.div_ceil(2) as u32, 1, t as u32),
6577 block_dim: (32, n_used as u32, 1),
6578 shared_mem_bytes: 0,
6579 };
6580 let (inf, outf, nu, ne, rbi) = (
6581 in_f as i32,
6582 out_f as i32,
6583 n_used as i32,
6584 n_expert as i32,
6585 rb as i64,
6586 );
6587 let __s_b = self.gpu.stream();
6588 let mut b = __s_b.launch_builder(&f);
6589 b.arg(table)
6590 .arg(sel)
6591 .arg(w)
6592 .arg(aq2)
6593 .arg(ad2)
6594 .arg(dst)
6595 .arg(&inf)
6596 .arg(&outf)
6597 .arg(&nu)
6598 .arg(&ne)
6599 .arg(&qt)
6600 .arg(&rbi);
6601 unsafe {
6602 b.launch(cfg)?;
6603 }
6604 Ok(())
6605 }
6606
6607 #[allow(clippy::too_many_arguments)]
6611 pub fn moe_gate_up_silu8_dev_q8_csr(
6612 &self,
6613 table: &CudaSlice<u64>,
6614 sel: &CudaSlice<i32>,
6615 aq: &CudaSlice<i8>,
6616 ad: &CudaSlice<f32>,
6617 n_pairs: usize,
6618 in_f: usize,
6619 n_ff: usize,
6620 n_used: usize,
6621 n_expert: usize,
6622 qt_g: i32,
6623 qt_u: i32,
6624 rb_g: usize,
6625 rb_u: usize,
6626 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6627 let f = if qt_g == crate::QT_NVFP4 {
6630 self.func("moe_gate_up_silu8_dev_q8_csr_nvfp4")
6631 } else {
6632 self.func("moe_gate_up_silu8_dev_q8_csr_iq4")
6633 };
6634 let mut act = self.alloc_uninit::<f32>(n_pairs * n_ff)?;
6635 let cfg = LaunchConfig {
6636 grid_dim: (n_ff as u32, n_pairs as u32, 1),
6637 block_dim: (32, 1, 1),
6638 shared_mem_bytes: 0,
6639 };
6640 let (inf, nff, ne, nu, npi, rbg, rbu) = (
6641 in_f as i32,
6642 n_ff as i32,
6643 n_expert as i32,
6644 n_used as i32,
6645 n_pairs as i32,
6646 rb_g as i64,
6647 rb_u as i64,
6648 );
6649 let __s_b = self.gpu.stream();
6650 let mut b = __s_b.launch_builder(&f);
6651 b.arg(table)
6652 .arg(sel)
6653 .arg(aq)
6654 .arg(ad)
6655 .arg(&mut act)
6656 .arg(&inf)
6657 .arg(&nff)
6658 .arg(&ne)
6659 .arg(&qt_g)
6660 .arg(&qt_u)
6661 .arg(&rbg)
6662 .arg(&rbu)
6663 .arg(&nu)
6664 .arg(&npi);
6665 unsafe {
6666 b.launch(cfg)?;
6667 }
6668 Ok(act)
6669 }
6670
6671 #[allow(clippy::too_many_arguments)]
6675 pub fn moe_down8_fma_dev_q8_variant(
6676 &self,
6677 variant: &str,
6678 table: &CudaSlice<u64>,
6679 sel: &cudarc::driver::CudaView<i32>,
6680 w: &cudarc::driver::CudaView<f32>,
6681 aq2: &CudaSlice<i8>,
6682 ad2: &CudaSlice<f32>,
6683 dst: &mut cudarc::driver::CudaViewMut<f32>,
6684 in_f: usize,
6685 out_f: usize,
6686 n_used: usize,
6687 n_expert: usize,
6688 qt: i32,
6689 rb: usize,
6690 ) -> Result<(), Box<dyn std::error::Error>> {
6691 let (inf, outf, nu, ne, rbi) = (
6692 in_f as i32,
6693 out_f as i32,
6694 n_used as i32,
6695 n_expert as i32,
6696 rb as i64,
6697 );
6698 let (f, cfg) = match variant {
6699 "w8h2" | "w8h2v" => (
6700 self.func(if variant == "w8h2" {
6701 "moe_down8_fma_dev_q8_w8h2"
6702 } else {
6703 "moe_down8_fma_dev_q8_w8h2v"
6704 }),
6705 LaunchConfig {
6706 grid_dim: (out_f.div_ceil(2) as u32, 1, 1),
6707 block_dim: (32, n_used as u32, 1),
6708 shared_mem_bytes: 0,
6709 },
6710 ),
6711 "w8h2r2" | "w8h2r2v" => (
6712 self.func(if variant == "w8h2r2" {
6713 "moe_down8_fma_dev_q8_w8h2r2"
6714 } else {
6715 "moe_down8_fma_dev_q8_w8h2r2v"
6716 }),
6717 LaunchConfig {
6718 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
6719 block_dim: (32, n_used as u32, 1),
6720 shared_mem_bytes: 0,
6721 },
6722 ),
6723 _ => (
6724 self.func("moe_down8_fma_dev_q8"),
6725 LaunchConfig {
6726 grid_dim: (out_f as u32, 1, 1),
6727 block_dim: (32, 1, 1),
6728 shared_mem_bytes: 0,
6729 },
6730 ),
6731 };
6732 let __s_b = self.gpu.stream();
6733 let mut b = __s_b.launch_builder(&f);
6734 b.arg(table)
6735 .arg(sel)
6736 .arg(w)
6737 .arg(aq2)
6738 .arg(ad2)
6739 .arg(dst)
6740 .arg(&inf)
6741 .arg(&outf)
6742 .arg(&nu)
6743 .arg(&ne)
6744 .arg(&qt)
6745 .arg(&rbi);
6746 unsafe {
6747 b.launch(cfg)?;
6748 }
6749 Ok(())
6750 }
6751
6752 #[allow(clippy::too_many_arguments)]
6754 pub fn moe_gate_up_silu8_dev_q8_variant(
6755 &self,
6756 variant: &str,
6757 table: &CudaSlice<u64>,
6758 sel: &cudarc::driver::CudaView<i32>,
6759 aq: &CudaSlice<i8>,
6760 ad: &CudaSlice<f32>,
6761 in_f: usize,
6762 n_ff: usize,
6763 n_used: usize,
6764 n_expert: usize,
6765 qt_g: i32,
6766 qt_u: i32,
6767 rb_g: usize,
6768 rb_u: usize,
6769 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6770 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?;
6771 let (inf, nff, ne, rbg, rbu) = (
6772 in_f as i32,
6773 n_ff as i32,
6774 n_expert as i32,
6775 rb_g as i64,
6776 rb_u as i64,
6777 );
6778 let f = self.func(if variant == "v" {
6779 "moe_gate_up_silu8_dev_q8_v"
6780 } else {
6781 "moe_gate_up_silu8_dev_q8"
6782 });
6783 let cfg = LaunchConfig {
6784 grid_dim: (n_ff as u32, n_used as u32, 1),
6785 block_dim: (32, 1, 1),
6786 shared_mem_bytes: 0,
6787 };
6788 let __s_b = self.gpu.stream();
6789 let mut b = __s_b.launch_builder(&f);
6790 b.arg(table)
6791 .arg(sel)
6792 .arg(aq)
6793 .arg(ad)
6794 .arg(&mut act)
6795 .arg(&inf)
6796 .arg(&nff)
6797 .arg(&ne)
6798 .arg(&qt_g)
6799 .arg(&qt_u)
6800 .arg(&rbg)
6801 .arg(&rbu);
6802 unsafe {
6803 b.launch(cfg)?;
6804 }
6805 Ok(act)
6806 }
6807
6808 pub fn moe_gate_up_silu8_dev(
6809 &self,
6810 table: &CudaSlice<u64>,
6811 sel: &cudarc::driver::CudaView<i32>,
6812 x: &cudarc::driver::CudaView<f32>,
6813 in_f: usize,
6814 n_ff: usize,
6815 n_used: usize,
6816 n_expert: usize,
6817 qt_g: i32,
6818 qt_u: i32,
6819 rb_g: usize,
6820 rb_u: usize,
6821 macros: &CudaSlice<f32>,
6822 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
6823 let f = self.func("moe_gate_up_silu8_dev");
6824 let mut act = self.alloc_uninit::<f32>(n_used * n_ff)?; let cfg = LaunchConfig {
6826 grid_dim: (n_ff as u32, n_used as u32, 1),
6827 block_dim: (256, 1, 1),
6828 shared_mem_bytes: 0,
6829 };
6830 let (inf, nff, ne, rbg, rbu) = (
6831 in_f as i32,
6832 n_ff as i32,
6833 n_expert as i32,
6834 rb_g as i64,
6835 rb_u as i64,
6836 );
6837 let __s_b = self.gpu.stream();
6838 let mut b = __s_b.launch_builder(&f);
6839 b.arg(table)
6840 .arg(sel)
6841 .arg(x)
6842 .arg(&mut act)
6843 .arg(&inf)
6844 .arg(&nff)
6845 .arg(&ne)
6846 .arg(&qt_g)
6847 .arg(&qt_u)
6848 .arg(&rbg)
6849 .arg(&rbu)
6850 .arg(macros);
6851 unsafe {
6852 b.launch(cfg)?;
6853 }
6854 Ok(act)
6855 }
6856
6857 #[allow(clippy::too_many_arguments)]
6860 pub fn moe_down8_fma_dev(
6861 &self,
6862 table: &CudaSlice<u64>,
6863 sel: &cudarc::driver::CudaView<i32>,
6864 w: &cudarc::driver::CudaView<f32>,
6865 act: &CudaSlice<f32>,
6866 dst: &mut cudarc::driver::CudaViewMut<f32>,
6867 in_f: usize,
6868 out_f: usize,
6869 n_used: usize,
6870 n_expert: usize,
6871 qt: i32,
6872 rb: usize,
6873 ) -> Result<(), Box<dyn std::error::Error>> {
6874 let f = self.func("moe_down8_fma_dev");
6875 let cfg = LaunchConfig {
6876 grid_dim: (out_f as u32, 1, 1),
6877 block_dim: (256, 1, 1),
6878 shared_mem_bytes: 0,
6879 };
6880 let (inf, outf, nu, ne, rbv) = (
6881 in_f as i32,
6882 out_f as i32,
6883 n_used as i32,
6884 n_expert as i32,
6885 rb as i64,
6886 );
6887 let __s_b = self.gpu.stream();
6888 let mut b = __s_b.launch_builder(&f);
6889 b.arg(table)
6890 .arg(sel)
6891 .arg(w)
6892 .arg(act)
6893 .arg(dst)
6894 .arg(&inf)
6895 .arg(&outf)
6896 .arg(&nu)
6897 .arg(&ne)
6898 .arg(&qt)
6899 .arg(&rbv);
6900 unsafe {
6901 b.launch(cfg)?;
6902 }
6903 Ok(())
6904 }
6905
6906 pub fn axpy_into(
6908 &self,
6909 src: &CudaSlice<f32>,
6910 alpha: f32,
6911 dst: &mut cudarc::driver::CudaViewMut<f32>,
6912 n: usize,
6913 ) -> Result<(), Box<dyn std::error::Error>> {
6914 let f = self.func("axpy_f32");
6915 let cfg = LaunchConfig::for_num_elems(n as u32);
6916 let (a, ni) = (alpha, n as i32);
6917 let __s_b = self.gpu.stream();
6918 let mut b = __s_b.launch_builder(&f);
6919 b.arg(src).arg(dst).arg(&a).arg(&ni);
6920 unsafe {
6921 b.launch(cfg)?;
6922 }
6923 Ok(())
6924 }
6925
6926 pub fn axpy_host_into(
6928 &self,
6929 src: &cudarc::driver::CudaView<'_, f32>,
6930 alpha: f32,
6931 dst: &mut cudarc::driver::CudaViewMut<f32>,
6932 n: usize,
6933 ) -> Result<(), Box<dyn std::error::Error>> {
6934 let f = self.func("axpy_host_f32");
6935 let cfg = LaunchConfig::for_num_elems(n as u32);
6936 let (a, ni) = (alpha, n as i32);
6937 let __s_b = self.gpu.stream();
6938 let mut b = __s_b.launch_builder(&f);
6939 b.arg(src).arg(dst).arg(&a).arg(&ni);
6940 unsafe {
6941 b.launch(cfg)?;
6942 }
6943 Ok(())
6944 }
6945
6946 pub fn add_scaled_rows(
6948 &self,
6949 src: &CudaSlice<f32>,
6950 scale: &CudaSlice<f32>,
6951 dst: &mut CudaSlice<f32>,
6952 ncols: usize,
6953 nrows: usize,
6954 ) -> Result<(), Box<dyn std::error::Error>> {
6955 let f = self.func("add_scaled_rows_f32");
6956 let cfg = LaunchConfig::for_num_elems((ncols * nrows) as u32);
6957 let (nc, nr) = (ncols as i32, nrows as i32);
6958 let __s_b = self.gpu.stream();
6959 let mut b = __s_b.launch_builder(&f);
6960 b.arg(src).arg(scale).arg(dst).arg(&nc).arg(&nr);
6961 unsafe {
6962 b.launch(cfg)?;
6963 }
6964 Ok(())
6965 }
6966
6967 pub fn gather_rows(
6971 &self,
6972 src: &CudaSlice<f32>,
6973 idx: &CudaSlice<i32>,
6974 dst: &mut CudaSlice<f32>,
6975 ncols: usize,
6976 m_e: usize,
6977 ) -> Result<(), Box<dyn std::error::Error>> {
6978 let f = self.func("gather_rows_f32");
6979 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
6980 let (nc, me) = (ncols as i32, m_e as i32);
6981 let __s_b = self.gpu.stream();
6982 let mut b = __s_b.launch_builder(&f);
6983 b.arg(src).arg(idx).arg(dst).arg(&nc).arg(&me);
6984 unsafe {
6985 b.launch(cfg)?;
6986 }
6987 Ok(())
6988 }
6989
6990 pub fn scatter_slot(
6995 &self,
6996 src: &CudaSlice<f32>,
6997 tok_idx: &CudaSlice<i32>,
6998 slot_idx: &CudaSlice<i32>,
6999 weight: &CudaSlice<f32>,
7000 dst: &mut CudaSlice<f32>,
7001 wbuf: &mut CudaSlice<f32>,
7002 ncols: usize,
7003 n_used: usize,
7004 m_e: usize,
7005 ) -> Result<(), Box<dyn std::error::Error>> {
7006 let f = self.func("scatter_add_slot_f32");
7007 let cfg = LaunchConfig::for_num_elems((m_e * ncols) as u32);
7008 let (nc, nu, me) = (ncols as i32, n_used as i32, m_e as i32);
7009 let __s_b = self.gpu.stream();
7010 let mut b = __s_b.launch_builder(&f);
7011 b.arg(src)
7012 .arg(tok_idx)
7013 .arg(slot_idx)
7014 .arg(weight)
7015 .arg(dst)
7016 .arg(wbuf)
7017 .arg(&nc)
7018 .arg(&nu)
7019 .arg(&me);
7020 unsafe {
7021 b.launch(cfg)?;
7022 }
7023 Ok(())
7024 }
7025
7026 pub fn reduce_slots(
7030 &self,
7031 slots: &CudaSlice<f32>,
7032 wbuf: &CudaSlice<f32>,
7033 dst: &mut CudaSlice<f32>,
7034 ncols: usize,
7035 n_used: usize,
7036 t: usize,
7037 ) -> Result<(), Box<dyn std::error::Error>> {
7038 let f = self.func("reduce_slots_f32");
7039 let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
7040 let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
7041 let __s_b = self.gpu.stream();
7042 let mut b = __s_b.launch_builder(&f);
7043 b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
7044 unsafe {
7045 b.launch(cfg)?;
7046 }
7047 Ok(())
7048 }
7049
7050 pub fn reduce_slots_host(
7055 &self,
7056 slots: &CudaSlice<f32>,
7057 wbuf: &CudaSlice<f32>,
7058 dst: &mut CudaSlice<f32>,
7059 ncols: usize,
7060 n_used: usize,
7061 t: usize,
7062 ) -> Result<(), Box<dyn std::error::Error>> {
7063 let f = self.func("reduce_slots_host_f32");
7064 let cfg = LaunchConfig::for_num_elems((t * ncols) as u32);
7065 let (nc, nu, ti) = (ncols as i32, n_used as i32, t as i32);
7066 let __s_b = self.gpu.stream();
7067 let mut b = __s_b.launch_builder(&f);
7068 b.arg(slots).arg(wbuf).arg(dst).arg(&nc).arg(&nu).arg(&ti);
7069 unsafe {
7070 b.launch(cfg)?;
7071 }
7072 Ok(())
7073 }
7074
7075 pub fn quantize_q8_1_view(
7082 &self,
7083 x: &cudarc::driver::CudaView<f32>,
7084 m: usize,
7085 in_f: usize,
7086 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7087 let f = self.func("quantize_q8_1");
7088 let nblk = in_f / 32;
7089 let mut q = self.alloc_uninit::<i8>(m * in_f)?;
7090 let mut d = self.alloc_uninit::<f32>(m * nblk)?;
7091 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
7092 let (inf, mi) = (in_f as i32, m as i32);
7093 let __s_b = self.gpu.stream();
7094 let mut b = __s_b.launch_builder(&f);
7095 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
7096 unsafe {
7097 b.launch(cfg)?;
7098 }
7099 Ok((q, d))
7100 }
7101
7102 pub fn quantize_q8_1(
7103 &self,
7104 x: &CudaSlice<f32>,
7105 m: usize,
7106 in_f: usize,
7107 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
7108 let nblk = in_f / 32;
7109 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);
7113 let (inf, mi) = (in_f as i32, m as i32);
7114 if Self::pdl_on() && Self::pdl_wb_on() {
7115 {
7116 use cudarc::driver::{DevicePtr, DevicePtrMut};
7117 let s = &self.gpu.stream();
7118 let (px, _g0) = x.device_ptr(s);
7119 let (pq, _g1) = q.device_ptr_mut(s);
7120 let (pd, _g2) = d.device_ptr_mut(s);
7121 let mut ps = [
7122 &px as *const _ as *mut std::ffi::c_void,
7123 &pq as *const _ as *mut _,
7124 &pd as *const _ as *mut _,
7125 &inf as *const _ as *mut _,
7126 &mi as *const _ as *mut _,
7127 ];
7128 unsafe {
7129 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
7130 }
7131 }
7132 return Ok((q, d));
7133 }
7134 let f = self.func("quantize_q8_1");
7135 let __s_b = self.gpu.stream();
7136 let mut b = __s_b.launch_builder(&f);
7137 b.arg(x).arg(&mut q).arg(&mut d).arg(&inf).arg(&mi);
7138 unsafe {
7139 b.launch(cfg)?;
7140 }
7141 Ok((q, d))
7142 }
7143
7144 pub fn quantize_fp4_act(
7148 &self,
7149 x: &CudaSlice<f32>,
7150 m: usize,
7151 in_f: usize,
7152 ) -> Result<(CudaSlice<u32>, CudaSlice<u8>), Box<dyn std::error::Error>> {
7153 let f = self.func("quantize_fp4_act");
7154 let nb16 = in_f / 16;
7155 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);
7158 let (inf, mi) = (in_f as i32, m as i32);
7159 let __s_b = self.gpu.stream();
7160 let mut b = __s_b.launch_builder(&f);
7161 b.arg(x).arg(&mut aq4).arg(&mut ad4).arg(&inf).arg(&mi);
7162 unsafe {
7163 b.launch(cfg)?;
7164 }
7165 Ok((aq4, ad4))
7166 }
7167
7168 pub fn qmatvec_gemm_nvfp4_fp4(
7173 &self,
7174 bytes: &CudaSlice<u8>,
7175 x: &CudaSlice<f32>,
7176 m: usize,
7177 in_f: usize,
7178 out_f: usize,
7179 row_bytes: usize,
7180 scale: f32,
7181 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7182 assert!(
7183 in_f % 64 == 0,
7184 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
7185 );
7186 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
7187 let mut y = self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)?;
7188 if scale != 1.0 {
7189 self.scale_inplace(&mut y, scale, m * out_f)?;
7190 }
7191 Ok(y)
7192 }
7193
7194 fn fp4_gemm_launch(
7197 &self,
7198 bytes: &CudaSlice<u8>,
7199 aq4: &CudaSlice<u32>,
7200 ad4: &CudaSlice<u8>,
7201 m: usize,
7202 in_f: usize,
7203 out_f: usize,
7204 row_bytes: usize,
7205 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7206 let f = self.func("qmatvec_gemm_nvfp4_fp4");
7207 let mut y = self.alloc_uninit::<f32>(m * out_f)?; const BM: u32 = 64;
7209 const BN: u32 = 256;
7210 let cfg = LaunchConfig {
7211 grid_dim: ((out_f as u32 + BM - 1) / BM, (m as u32 + BN - 1) / BN, 1),
7212 block_dim: (32, 4, 1),
7213 shared_mem_bytes: 0,
7214 };
7215 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7216 let __s_b = self.gpu.stream();
7217 let mut b = __s_b.launch_builder(&f);
7218 b.arg(bytes)
7219 .arg(aq4)
7220 .arg(ad4)
7221 .arg(&mut y)
7222 .arg(&inf)
7223 .arg(&outf)
7224 .arg(&mi)
7225 .arg(&rb);
7226 unsafe {
7227 b.launch(cfg)?;
7228 }
7229 Ok(y)
7230 }
7231
7232 pub fn qmatvec_gemm_nvfp4_fp4_raw(
7234 &self,
7235 bytes: &CudaSlice<u8>,
7236 x: &CudaSlice<f32>,
7237 m: usize,
7238 in_f: usize,
7239 out_f: usize,
7240 row_bytes: usize,
7241 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7242 assert!(
7243 in_f % 64 == 0,
7244 "FP4 GEMM requires in_f % 64 == 0, got {in_f}"
7245 );
7246 let (aq4, ad4) = self.quantize_fp4_act(x, m, in_f)?;
7247 self.fp4_gemm_launch(bytes, &aq4, &ad4, m, in_f, out_f, row_bytes)
7248 }
7249
7250 pub fn qmatvec_q8_0_fast(
7252 &self,
7253 w: &CudaSlice<u8>,
7254 x: &CudaSlice<f32>,
7255 m: usize,
7256 in_f: usize,
7257 out_f: usize,
7258 row_bytes: usize,
7259 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7260 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
7261 let f = self.func("qmatvec_q8_0_dp4a");
7262 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
7264 grid_dim: (out_f as u32, m as u32, 1),
7265 block_dim: (128, 1, 1),
7266 shared_mem_bytes: 0,
7267 };
7268 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7269 let __s_b = self.gpu.stream();
7270 let mut b = __s_b.launch_builder(&f);
7271 b.arg(w)
7272 .arg(&aq)
7273 .arg(&ad)
7274 .arg(&mut y)
7275 .arg(&inf)
7276 .arg(&outf)
7277 .arg(&mi)
7278 .arg(&rb);
7279 unsafe {
7280 b.launch(cfg)?;
7281 }
7282 Ok(y)
7283 }
7284
7285 #[allow(non_snake_case)] pub fn qmatvec_q4_K_fast(
7288 &self,
7289 w: &CudaSlice<u8>,
7290 x: &CudaSlice<f32>,
7291 m: usize,
7292 in_f: usize,
7293 out_f: usize,
7294 row_bytes: usize,
7295 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7296 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
7297 let f = self.func("qmatvec_q4_K_dp4a");
7298 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
7300 grid_dim: (out_f as u32, m as u32, 1),
7301 block_dim: (128, 1, 1),
7302 shared_mem_bytes: 0,
7303 };
7304 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7305 let __s_b = self.gpu.stream();
7306 let mut b = __s_b.launch_builder(&f);
7307 b.arg(w)
7308 .arg(&aq)
7309 .arg(&ad)
7310 .arg(&mut y)
7311 .arg(&inf)
7312 .arg(&outf)
7313 .arg(&mi)
7314 .arg(&rb);
7315 unsafe {
7316 b.launch(cfg)?;
7317 }
7318 Ok(y)
7319 }
7320
7321 #[allow(non_snake_case)] pub fn qmatvec_q6_K_fast(
7324 &self,
7325 w: &CudaSlice<u8>,
7326 x: &CudaSlice<f32>,
7327 m: usize,
7328 in_f: usize,
7329 out_f: usize,
7330 row_bytes: usize,
7331 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7332 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
7333 let f = self.func("qmatvec_q6_K_dp4a");
7334 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
7336 grid_dim: (out_f as u32, m as u32, 1),
7337 block_dim: (128, 1, 1),
7338 shared_mem_bytes: 0,
7339 };
7340 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7341 let __s_b = self.gpu.stream();
7342 let mut b = __s_b.launch_builder(&f);
7343 b.arg(w)
7344 .arg(&aq)
7345 .arg(&ad)
7346 .arg(&mut y)
7347 .arg(&inf)
7348 .arg(&outf)
7349 .arg(&mi)
7350 .arg(&rb);
7351 unsafe {
7352 b.launch(cfg)?;
7353 }
7354 Ok(y)
7355 }
7356
7357 #[allow(non_snake_case)] pub fn qmatvec_q5_K_fast(
7360 &self,
7361 w: &CudaSlice<u8>,
7362 x: &CudaSlice<f32>,
7363 m: usize,
7364 in_f: usize,
7365 out_f: usize,
7366 row_bytes: usize,
7367 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7368 self.qmatvec_dp4a_named(
7369 "qmatvec_q5_K_dp4a",
7370 &w.slice(0..w.len()),
7371 x,
7372 m,
7373 in_f,
7374 out_f,
7375 row_bytes,
7376 )
7377 }
7378 #[allow(non_snake_case)] pub fn qmatvec_q3_K_fast(
7381 &self,
7382 w: &CudaSlice<u8>,
7383 x: &CudaSlice<f32>,
7384 m: usize,
7385 in_f: usize,
7386 out_f: usize,
7387 row_bytes: usize,
7388 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7389 self.qmatvec_dp4a_named(
7390 "qmatvec_q3_K_dp4a",
7391 &w.slice(0..w.len()),
7392 x,
7393 m,
7394 in_f,
7395 out_f,
7396 row_bytes,
7397 )
7398 }
7399 pub fn qmatvec_nvfp4_fast_rp(
7401 &self,
7402 w: &CudaSlice<u8>,
7403 x: &CudaSlice<f32>,
7404 m: usize,
7405 in_f: usize,
7406 out_f: usize,
7407 row_bytes: usize,
7408 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7409 assert!(
7410 in_f % 64 == 0,
7411 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
7412 );
7413 self.qmatvec_dp4a_named(
7414 "qmatvec_nvfp4_dp4a_rp",
7415 &w.slice(0..w.len()),
7416 x,
7417 m,
7418 in_f,
7419 out_f,
7420 row_bytes,
7421 )
7422 }
7423 pub fn qmatvec_nvfp4_fast(
7425 &self,
7426 w: &cudarc::driver::CudaView<'_, u8>,
7427 x: &CudaSlice<f32>,
7428 m: usize,
7429 in_f: usize,
7430 out_f: usize,
7431 row_bytes: usize,
7432 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7433 assert!(
7436 in_f % 64 == 0,
7437 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
7438 );
7439 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a", w, x, m, in_f, out_f, row_bytes)
7440 }
7441 pub fn qmatvec_nvfp4_fast_v2(
7444 &self,
7445 w: &cudarc::driver::CudaView<'_, u8>,
7446 x: &CudaSlice<f32>,
7447 m: usize,
7448 in_f: usize,
7449 out_f: usize,
7450 row_bytes: usize,
7451 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7452 assert!(
7453 in_f % 64 == 0,
7454 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
7455 );
7456 self.qmatvec_dp4a_named("qmatvec_nvfp4_dp4a_v2", w, x, m, in_f, out_f, row_bytes)
7457 }
7458 #[allow(non_snake_case)] pub fn qmatvec_iq4_XS_fast(
7461 &self,
7462 w: &CudaSlice<u8>,
7463 x: &CudaSlice<f32>,
7464 m: usize,
7465 in_f: usize,
7466 out_f: usize,
7467 row_bytes: usize,
7468 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7469 self.qmatvec_dp4a_named(
7470 "qmatvec_iq4_XS_dp4a",
7471 &w.slice(0..w.len()),
7472 x,
7473 m,
7474 in_f,
7475 out_f,
7476 row_bytes,
7477 )
7478 }
7479
7480 fn qmatvec_dp4a_named(
7482 &self,
7483 name: &str,
7484 w: &cudarc::driver::CudaView<'_, u8>,
7485 x: &CudaSlice<f32>,
7486 m: usize,
7487 in_f: usize,
7488 out_f: usize,
7489 row_bytes: usize,
7490 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
7491 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
7492 let f = self.func(name);
7493 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
7495 grid_dim: (out_f as u32, m as u32, 1),
7496 block_dim: (128, 1, 1),
7497 shared_mem_bytes: 0,
7498 };
7499 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7500 let __s_b = self.gpu.stream();
7501 let mut b = __s_b.launch_builder(&f);
7502 b.arg(w)
7503 .arg(&aq)
7504 .arg(&ad)
7505 .arg(&mut y)
7506 .arg(&inf)
7507 .arg(&outf)
7508 .arg(&mi)
7509 .arg(&rb);
7510 unsafe {
7511 b.launch(cfg)?;
7512 }
7513 Ok(y)
7514 }
7515
7516 #[allow(clippy::too_many_arguments)]
7522 pub fn qmatvec_nvfp4_fast_prequant_into(
7523 &self,
7524 w: &CudaSlice<u8>,
7525 aq: &CudaSlice<i8>,
7526 ad: &CudaSlice<f32>,
7527 y: &mut CudaSlice<f32>,
7528 m: usize,
7529 in_f: usize,
7530 out_f: usize,
7531 row_bytes: usize,
7532 ) -> Result<(), Box<dyn std::error::Error>> {
7533 assert!(
7534 in_f % 64 == 0,
7535 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
7536 );
7537 if y.len() < m * out_f {
7538 return Err(format!(
7539 "NVFP4 prequant output {} is shorter than {m}x{out_f}",
7540 y.len()
7541 )
7542 .into());
7543 }
7544 let f = self.func("qmatvec_nvfp4_dp4a");
7545 let cfg = LaunchConfig {
7546 grid_dim: (out_f as u32, m as u32, 1),
7547 block_dim: (128, 1, 1),
7548 shared_mem_bytes: 0,
7549 };
7550 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
7551 let __s_b = self.gpu.stream();
7552 let mut b = __s_b.launch_builder(&f);
7553 b.arg(w)
7554 .arg(aq)
7555 .arg(ad)
7556 .arg(y)
7557 .arg(&inf)
7558 .arg(&outf)
7559 .arg(&mi)
7560 .arg(&rb);
7561 unsafe {
7562 b.launch(cfg)?;
7563 }
7564 Ok(())
7565 }
7566
7567 #[allow(clippy::too_many_arguments)]
7570 pub fn matvec_f32_qkv_into(
7571 &self,
7572 wq: &CudaSlice<f32>,
7573 wk: &CudaSlice<f32>,
7574 wv: &CudaSlice<f32>,
7575 wg: &CudaSlice<f32>,
7576 x: &CudaSlice<f32>,
7577 yq: &mut CudaSlice<f32>,
7578 yk: &mut CudaSlice<f32>,
7579 yv: &mut CudaSlice<f32>,
7580 yg: &mut CudaSlice<f32>,
7581 in_f: usize,
7582 out_q: usize,
7583 out_kv: usize,
7584 out_g: usize,
7585 ) -> Result<(), Box<dyn std::error::Error>> {
7586 if in_f % 4 != 0
7587 || wq.len() != out_q * in_f
7588 || wk.len() != out_kv * in_f
7589 || wv.len() != out_kv * in_f
7590 || wg.len() < out_g * in_f
7591 || x.len() < in_f
7592 || yq.len() < out_q
7593 || yk.len() < out_kv
7594 || yv.len() < out_kv
7595 || (out_g > 0 && yg.len() < out_g)
7596 {
7597 return Err(format!(
7598 "fused QKV geometry in={in_f} out_q={out_q} out_kv={out_kv} out_g={out_g} \
7599 wq={} wk={} wv={} wg={}",
7600 wq.len(),
7601 wk.len(),
7602 wv.len(),
7603 wg.len()
7604 )
7605 .into());
7606 }
7607 let f = self.func("matvec_f32_qkv");
7608 let cfg = LaunchConfig {
7609 grid_dim: ((out_q + 2 * out_kv + out_g) as u32, 1, 1),
7610 block_dim: (128, 1, 1),
7611 shared_mem_bytes: 0,
7612 };
7613 let (inf, oq, okv, og) = (in_f as i32, out_q as i32, out_kv as i32, out_g as i32);
7614 let __s_b = self.gpu.stream();
7615 let mut b = __s_b.launch_builder(&f);
7616 b.arg(wq)
7617 .arg(wk)
7618 .arg(wv)
7619 .arg(wg)
7620 .arg(x)
7621 .arg(yq)
7622 .arg(yk)
7623 .arg(yv)
7624 .arg(yg)
7625 .arg(&inf)
7626 .arg(&oq)
7627 .arg(&okv)
7628 .arg(&og);
7629 unsafe {
7630 b.launch(cfg)?;
7631 }
7632 Ok(())
7633 }
7634
7635 #[allow(clippy::too_many_arguments)]
7641 #[allow(clippy::too_many_arguments)]
7644 pub fn qmatvec_nvfp4_sel_gu_into(
7645 &self,
7646 gate_bank: &CudaSlice<u8>,
7647 up_bank: &CudaSlice<u8>,
7648 sel: &CudaSlice<i32>,
7649 aq: &CudaSlice<i8>,
7650 ad: &CudaSlice<f32>,
7651 yg: &mut CudaSlice<f32>,
7652 yu: &mut CudaSlice<f32>,
7653 n_sel: usize,
7654 in_f: usize,
7655 out_f: usize,
7656 row_bytes: usize,
7657 expert_stride: usize,
7658 ) -> Result<(), Box<dyn std::error::Error>> {
7659 assert!(in_f % 64 == 0, "NVFP4 dp4a requires in_f % 64 == 0");
7660 if yg.len() < n_sel * out_f || yu.len() < n_sel * out_f || sel.len() < n_sel {
7661 return Err("NVFP4 gu sel geometry".into());
7662 }
7663 static RPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
7666 let rpw = *RPW.get_or_init(|| {
7667 std::env::var("MEMRA_SEL_GU_RPW")
7668 .ok()
7669 .and_then(|v| v.parse().ok())
7670 .filter(|r| *r == 2 || *r == 4)
7671 .unwrap_or(1)
7672 });
7673 let rpw = if out_f % rpw == 0 { rpw } else { 1 };
7674 static WPR: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
7677 let wpr = *WPR.get_or_init(|| std::env::var("MEMRA_SEL_GU_WPR").as_deref() == Ok("1"));
7678 let f = self.func(match (wpr, rpw) {
7679 (true, _) => "qmatvec_nvfp4_dp4a_sel_v2_gu_wpr",
7680 (_, 4) => "qmatvec_nvfp4_dp4a_sel_v2_gu_r4",
7681 (_, 2) => "qmatvec_nvfp4_dp4a_sel_v2_gu_r2",
7682 _ => "qmatvec_nvfp4_dp4a_sel_v2_gu",
7683 });
7684 let cfg = LaunchConfig {
7685 grid_dim: if wpr {
7686 (((2 * out_f) as u32).div_ceil(4), n_sel as u32, 1)
7687 } else if rpw == 1 {
7688 ((2 * out_f) as u32, n_sel as u32, 1)
7689 } else {
7690 ((out_f / rpw) as u32, n_sel as u32, 1)
7691 },
7692 block_dim: if wpr { (32, 4, 1) } else { (128, 1, 1) },
7693 shared_mem_bytes: 0,
7694 };
7695 let (inf, outf, ns) = (in_f as i32, out_f as i32, n_sel as i32);
7696 let (rb, es) = (row_bytes as i64, expert_stride as i64);
7697 let (ars, adrs) = (0i64, 0i64);
7698 let __s_b = self.gpu.stream();
7699 let mut b = __s_b.launch_builder(&f);
7700 b.arg(gate_bank)
7701 .arg(up_bank)
7702 .arg(sel)
7703 .arg(aq)
7704 .arg(ad)
7705 .arg(yg)
7706 .arg(yu)
7707 .arg(&inf)
7708 .arg(&outf)
7709 .arg(&ns)
7710 .arg(&rb)
7711 .arg(&es)
7712 .arg(&ars)
7713 .arg(&adrs);
7714 unsafe {
7715 b.launch(cfg)?;
7716 }
7717 Ok(())
7718 }
7719
7720 #[allow(clippy::too_many_arguments)]
7728 pub fn qmatvec_nvfp4_sel_down8_into(
7729 &self,
7730 bank: &CudaSlice<u8>,
7731 sel: &CudaSlice<i32>,
7732 aq: &CudaSlice<i8>,
7733 ad: &CudaSlice<f32>,
7734 route_w: &CudaSlice<f32>,
7735 md: &CudaSlice<f32>,
7736 dst: &mut CudaSlice<f32>,
7737 n_sel: usize,
7738 in_f: usize,
7739 out_f: usize,
7740 row_bytes: usize,
7741 expert_stride: usize,
7742 act_row_stride: usize,
7743 ad_row_stride: usize,
7744 ) -> Result<(), Box<dyn std::error::Error>> {
7745 if in_f % 64 != 0
7746 || n_sel == 0
7747 || n_sel > 8
7748 || (in_f >> 5) > 32
7749 || dst.len() < out_f
7750 || sel.len() < n_sel
7751 || route_w.len() < n_sel
7752 {
7753 return Err(format!(
7754 "NVFP4 sel down8 geometry in_f={in_f} out_f={out_f} n_sel={n_sel} dst={}",
7755 dst.len()
7756 )
7757 .into());
7758 }
7759 if !crate::tp::nvfp4_bank_v2_on() {
7760 return Err("NVFP4 sel down8 requires the v2 banks (MEMRA_NVFP4_BANK_V2=1)".into());
7761 }
7762 let f = self.func("qmatvec_nvfp4_dp4a_sel_v2_down8");
7763 let cfg = LaunchConfig {
7764 grid_dim: (out_f as u32, 1, 1),
7765 block_dim: (32, n_sel as u32, 1),
7766 shared_mem_bytes: 0,
7767 };
7768 let (inf, outf, ns) = (in_f as i32, out_f as i32, n_sel as i32);
7769 let (rb, es) = (row_bytes as i64, expert_stride as i64);
7770 let (ars, adrs) = (act_row_stride as i64, ad_row_stride as i64);
7771 let __s_b = self.gpu.stream();
7772 let mut b = __s_b.launch_builder(&f);
7773 b.arg(bank)
7774 .arg(sel)
7775 .arg(aq)
7776 .arg(ad)
7777 .arg(route_w)
7778 .arg(md)
7779 .arg(dst)
7780 .arg(&inf)
7781 .arg(&outf)
7782 .arg(&ns)
7783 .arg(&rb)
7784 .arg(&es)
7785 .arg(&ars)
7786 .arg(&adrs);
7787 unsafe {
7788 b.launch(cfg)?;
7789 }
7790 Ok(())
7791 }
7792
7793 #[allow(clippy::too_many_arguments)]
7797 pub fn qmatvec_nvfp4_sel_down8_rows_into(
7798 &self,
7799 bank: &CudaSlice<u8>,
7800 sel: &CudaSlice<i32>,
7801 aq: &CudaSlice<i8>,
7802 ad: &CudaSlice<f32>,
7803 route_w: &CudaSlice<f32>,
7804 md: &CudaSlice<f32>,
7805 dst: &mut CudaSlice<f32>,
7806 t: usize,
7807 n_sel_col: usize,
7808 in_f: usize,
7809 out_f: usize,
7810 row_bytes: usize,
7811 expert_stride: usize,
7812 act_row_stride: usize,
7813 ad_row_stride: usize,
7814 ) -> Result<(), Box<dyn std::error::Error>> {
7815 let n_sel = t * n_sel_col;
7816 if in_f % 64 != 0
7817 || n_sel_col == 0
7818 || n_sel_col > 8
7819 || t == 0
7820 || t > 64
7821 || (in_f >> 5) > 32
7822 || dst.len() < t * out_f
7823 || sel.len() < n_sel
7824 || route_w.len() < n_sel
7825 {
7826 return Err(format!(
7827 "NVFP4 sel down8 rows geometry in_f={in_f} out_f={out_f} t={t} dst={}",
7828 dst.len()
7829 )
7830 .into());
7831 }
7832 if !crate::tp::nvfp4_bank_v2_on() {
7833 return Err(
7834 "NVFP4 sel down8 rows requires the v2 banks (MEMRA_NVFP4_BANK_V2=1)".into(),
7835 );
7836 }
7837 let f = self.func("qmatvec_nvfp4_dp4a_sel_v2_down8_rows");
7838 let cfg = LaunchConfig {
7839 grid_dim: (out_f as u32, t as u32, 1),
7840 block_dim: (32, n_sel_col as u32, 1),
7841 shared_mem_bytes: 0,
7842 };
7843 let (inf, outf, nsc) = (in_f as i32, out_f as i32, n_sel_col as i32);
7844 let (rb, es) = (row_bytes as i64, expert_stride as i64);
7845 let (ars, adrs) = (act_row_stride as i64, ad_row_stride as i64);
7846 let __s_b = self.gpu.stream();
7847 let mut b = __s_b.launch_builder(&f);
7848 b.arg(bank)
7849 .arg(sel)
7850 .arg(aq)
7851 .arg(ad)
7852 .arg(route_w)
7853 .arg(md)
7854 .arg(dst)
7855 .arg(&inf)
7856 .arg(&outf)
7857 .arg(&nsc)
7858 .arg(&rb)
7859 .arg(&es)
7860 .arg(&ars)
7861 .arg(&adrs);
7862 unsafe {
7863 b.launch(cfg)?;
7864 }
7865 Ok(())
7866 }
7867
7868 #[allow(clippy::too_many_arguments)]
7871 pub fn qmatvec_nvfp4_sel_gu_ep_into(
7872 &self,
7873 gate_bank: &CudaSlice<u8>,
7874 up_bank: &CudaSlice<u8>,
7875 sel: &CudaSlice<i32>,
7876 aq: &CudaSlice<i8>,
7877 ad: &CudaSlice<f32>,
7878 yg: &mut CudaSlice<f32>,
7879 yu: &mut CudaSlice<f32>,
7880 n_sel: usize,
7881 in_f: usize,
7882 out_f: usize,
7883 row_bytes: usize,
7884 expert_stride: usize,
7885 owner: usize,
7886 ) -> Result<(), Box<dyn std::error::Error>> {
7887 assert!(in_f % 64 == 0, "NVFP4 dp4a requires in_f % 64 == 0");
7888 if yg.len() < n_sel * out_f || yu.len() < n_sel * out_f || sel.len() < n_sel {
7889 return Err("NVFP4 gu ep geometry".into());
7890 }
7891 let f = self.func("qmatvec_nvfp4_dp4a_sel_v2_gu_ep");
7892 let cfg = LaunchConfig {
7893 grid_dim: ((2 * out_f) as u32, n_sel as u32, 1),
7894 block_dim: (128, 1, 1),
7895 shared_mem_bytes: 0,
7896 };
7897 let (inf, outf, ns, own) = (in_f as i32, out_f as i32, n_sel as i32, owner as i32);
7898 let (rb, es) = (row_bytes as i64, expert_stride as i64);
7899 let (ars, adrs) = (0i64, 0i64);
7900 let __s_b = self.gpu.stream();
7901 let mut b = __s_b.launch_builder(&f);
7902 b.arg(gate_bank)
7903 .arg(up_bank)
7904 .arg(sel)
7905 .arg(aq)
7906 .arg(ad)
7907 .arg(yg)
7908 .arg(yu)
7909 .arg(&inf)
7910 .arg(&outf)
7911 .arg(&ns)
7912 .arg(&rb)
7913 .arg(&es)
7914 .arg(&ars)
7915 .arg(&adrs)
7916 .arg(&own);
7917 unsafe {
7918 b.launch(cfg)?;
7919 }
7920 Ok(())
7921 }
7922
7923 #[allow(clippy::too_many_arguments)]
7925 pub fn silu_mul_scaled_q8_1_sel_ep_into(
7926 &self,
7927 gate: &CudaSlice<f32>,
7928 up: &CudaSlice<f32>,
7929 gmac: &CudaSlice<f32>,
7930 umac: &CudaSlice<f32>,
7931 sel: &CudaSlice<i32>,
7932 limit: Option<f32>,
7933 out_q: &mut CudaSlice<i8>,
7934 out_d: &mut CudaSlice<f32>,
7935 n_per: usize,
7936 n_sel: usize,
7937 owner: usize,
7938 ) -> Result<(), Box<dyn std::error::Error>> {
7939 if n_per % 32 != 0 || out_q.len() < n_sel * n_per || out_d.len() < n_sel * n_per / 32 {
7940 return Err("NVFP4 silu ep geometry".into());
7941 }
7942 let f = self.func("silu_mul_scaled_q8_1_sel_ep");
7943 let warps = n_sel * n_per / 32;
7944 let cfg = LaunchConfig {
7945 grid_dim: ((warps as u32).div_ceil(4), 1, 1),
7946 block_dim: (128, 1, 1),
7947 shared_mem_bytes: 0,
7948 };
7949 let (np, ns, own) = (n_per as i32, n_sel as i32, owner as i32);
7950 let (lim, has) = match limit {
7951 Some(l) => (l, 1i32),
7952 None => (0.0f32, 0i32),
7953 };
7954 let __s_b = self.gpu.stream();
7955 let mut b = __s_b.launch_builder(&f);
7956 b.arg(gate)
7957 .arg(up)
7958 .arg(gmac)
7959 .arg(umac)
7960 .arg(sel)
7961 .arg(&lim)
7962 .arg(&has)
7963 .arg(out_q)
7964 .arg(out_d)
7965 .arg(&np)
7966 .arg(&ns)
7967 .arg(&own);
7968 unsafe {
7969 b.launch(cfg)?;
7970 }
7971 Ok(())
7972 }
7973
7974 #[allow(clippy::too_many_arguments)]
7976 pub fn qmatvec_nvfp4_sel_down8_ep_into(
7977 &self,
7978 bank: &CudaSlice<u8>,
7979 sel: &CudaSlice<i32>,
7980 aq: &CudaSlice<i8>,
7981 ad: &CudaSlice<f32>,
7982 route_w: &CudaSlice<f32>,
7983 md: &CudaSlice<f32>,
7984 dst: &mut CudaSlice<f32>,
7985 n_sel: usize,
7986 in_f: usize,
7987 out_f: usize,
7988 row_bytes: usize,
7989 expert_stride: usize,
7990 act_row_stride: usize,
7991 ad_row_stride: usize,
7992 owner: usize,
7993 ) -> Result<(), Box<dyn std::error::Error>> {
7994 if in_f % 64 != 0 || n_sel == 0 || n_sel > 8 || (in_f >> 5) > 64 || dst.len() < out_f {
7995 return Err("NVFP4 down8 ep geometry".into());
7996 }
7997 let f = self.func("qmatvec_nvfp4_dp4a_sel_v2_down8_ep");
7998 let cfg = LaunchConfig {
7999 grid_dim: (out_f as u32, 1, 1),
8000 block_dim: (32, n_sel as u32, 1),
8001 shared_mem_bytes: 0,
8002 };
8003 let (inf, outf, ns, own) = (in_f as i32, out_f as i32, n_sel as i32, owner as i32);
8004 let (rb, es) = (row_bytes as i64, expert_stride as i64);
8005 let (ars, adrs) = (act_row_stride as i64, ad_row_stride as i64);
8006 let __s_b = self.gpu.stream();
8007 let mut b = __s_b.launch_builder(&f);
8008 b.arg(bank)
8009 .arg(sel)
8010 .arg(aq)
8011 .arg(ad)
8012 .arg(route_w)
8013 .arg(md)
8014 .arg(dst)
8015 .arg(&inf)
8016 .arg(&outf)
8017 .arg(&ns)
8018 .arg(&rb)
8019 .arg(&es)
8020 .arg(&ars)
8021 .arg(&adrs)
8022 .arg(&own);
8023 unsafe {
8024 b.launch(cfg)?;
8025 }
8026 Ok(())
8027 }
8028
8029 pub fn qmatvec_nvfp4_sel_into(
8030 &self,
8031 bank: &CudaSlice<u8>,
8032 sel: &CudaSlice<i32>,
8033 aq: &CudaSlice<i8>,
8034 ad: &CudaSlice<f32>,
8035 y: &mut CudaSlice<f32>,
8036 n_sel: usize,
8037 in_f: usize,
8038 out_f: usize,
8039 row_bytes: usize,
8040 expert_stride: usize,
8041 act_row_stride: usize,
8042 ad_row_stride: usize,
8043 ) -> Result<(), Box<dyn std::error::Error>> {
8044 assert!(
8045 in_f % 64 == 0,
8046 "NVFP4 dp4a requires in_f % 64 == 0, got {in_f}"
8047 );
8048 if y.len() < n_sel * out_f || sel.len() < n_sel {
8049 return Err(format!(
8050 "NVFP4 sel output {} / sel {} shorter than {n_sel}x{out_f}",
8051 y.len(),
8052 sel.len()
8053 )
8054 .into());
8055 }
8056 static MR: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
8063 let mode = *MR.get_or_init(|| {
8064 if crate::tp::nvfp4_bank_v2_on() {
8065 3
8066 } else if std::env::var("MEMRA_SEL_STREAM").as_deref() == Ok("1") {
8067 2
8068 } else if std::env::var("MEMRA_SEL_MR").as_deref() == Ok("1") {
8069 1
8070 } else {
8071 0
8072 }
8073 });
8074 let mode = if mode == 2 && in_f > 4096 { 0 } else { mode };
8075 static V2S: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8079 let v2s = mode == 3
8080 && *V2S.get_or_init(|| std::env::var("MEMRA_SEL_V2S").as_deref() == Ok("1"))
8081 && row_bytes % 16 == 0
8082 && in_f <= 4096;
8083 let f = match (mode, v2s) {
8084 (3, true) => self.func("qmatvec_nvfp4_dp4a_sel_v2s"),
8085 (3, false) => self.func("qmatvec_nvfp4_dp4a_sel_v2"),
8086 (2, _) => self.func("qmatvec_nvfp4_dp4a_sel_stream"),
8087 (1, _) => self.func("qmatvec_nvfp4_dp4a_sel_mr4"),
8088 _ => self.func("qmatvec_nvfp4_dp4a_sel"),
8089 };
8090 let nsb = in_f >> 5;
8095 let fit_block: u32 = if (mode == 0 || mode == 3) && !v2s && nsb <= 32 {
8096 32
8097 } else if mode == 1 {
8098 512
8099 } else {
8100 128
8101 };
8102 let cfg = LaunchConfig {
8103 grid_dim: (
8104 if v2s {
8105 (out_f as u32).div_ceil(8)
8106 } else {
8107 match mode {
8108 2 => (out_f as u32).div_ceil(16),
8109 1 => (out_f as u32).div_ceil(4),
8110 _ => out_f as u32,
8111 }
8112 },
8113 n_sel as u32,
8114 1,
8115 ),
8116 block_dim: (fit_block, 1, 1),
8117 shared_mem_bytes: 0,
8118 };
8119 let (inf, outf, ns) = (in_f as i32, out_f as i32, n_sel as i32);
8120 let (rb, es, ars, adrs) = (
8121 row_bytes as i64,
8122 expert_stride as i64,
8123 act_row_stride as i64,
8124 ad_row_stride as i64,
8125 );
8126 let __s_b = self.gpu.stream();
8127 let mut b = __s_b.launch_builder(&f);
8128 b.arg(bank)
8129 .arg(sel)
8130 .arg(aq)
8131 .arg(ad)
8132 .arg(y)
8133 .arg(&inf)
8134 .arg(&outf)
8135 .arg(&ns)
8136 .arg(&rb)
8137 .arg(&es)
8138 .arg(&ars)
8139 .arg(&adrs);
8140 unsafe {
8141 b.launch(cfg)?;
8142 }
8143 Ok(())
8144 }
8145
8146 #[allow(clippy::too_many_arguments)]
8151 pub fn silu_mul_scaled_q8_1_sel_into(
8152 &self,
8153 gate: &CudaSlice<f32>,
8154 up: &CudaSlice<f32>,
8155 gmac: &CudaSlice<f32>,
8156 umac: &CudaSlice<f32>,
8157 sel: &CudaSlice<i32>,
8158 limit: Option<f32>,
8159 out_q: &mut CudaSlice<i8>,
8160 out_d: &mut CudaSlice<f32>,
8161 n_per: usize,
8162 n_sel: usize,
8163 ) -> Result<(), Box<dyn std::error::Error>> {
8164 let n = n_per * n_sel;
8165 if n_per % 32 != 0 || out_q.len() < n || out_d.len() < n / 32 {
8166 return Err(format!(
8167 "silu sel geometry n_per={n_per} n_sel={n_sel} q={} d={}",
8168 out_q.len(),
8169 out_d.len()
8170 )
8171 .into());
8172 }
8173 if let Some(limit) = limit {
8174 if limit <= 1e-6 {
8175 return Err(format!(
8176 "silu sel clamp limit {limit} is at or below the 1e-6 eps gate"
8177 )
8178 .into());
8179 }
8180 let f = self.func("silu_mul_scaled_q8_1_sel_clamp");
8181 let cfg = LaunchConfig::for_num_elems(n as u32);
8182 let (np, ns) = (n_per as i32, n_sel as i32);
8183 let __s_b = self.gpu.stream();
8184 let mut b = __s_b.launch_builder(&f);
8185 b.arg(gate)
8186 .arg(up)
8187 .arg(gmac)
8188 .arg(umac)
8189 .arg(sel)
8190 .arg(&limit)
8191 .arg(out_q)
8192 .arg(out_d)
8193 .arg(&np)
8194 .arg(&ns);
8195 unsafe {
8196 b.launch(cfg)?;
8197 }
8198 return Ok(());
8199 }
8200 let f = self.func("silu_mul_scaled_q8_1_sel");
8201 let cfg = LaunchConfig::for_num_elems(n as u32);
8202 let (np, ns) = (n_per as i32, n_sel as i32);
8203 let __s_b = self.gpu.stream();
8204 let mut b = __s_b.launch_builder(&f);
8205 b.arg(gate)
8206 .arg(up)
8207 .arg(gmac)
8208 .arg(umac)
8209 .arg(sel)
8210 .arg(out_q)
8211 .arg(out_d)
8212 .arg(&np)
8213 .arg(&ns);
8214 unsafe {
8215 b.launch(cfg)?;
8216 }
8217 Ok(())
8218 }
8219
8220 pub fn htod(&self, v: &[f32]) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8221 Ok(self.gpu.stream().clone_htod(v)?)
8222 }
8223 pub fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
8224 Ok(self.gpu.stream().clone_htod(v)?)
8225 }
8226 pub fn htod_i8(&self, v: &[i8]) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
8228 Ok(self.gpu.stream().clone_htod(v)?)
8229 }
8230 pub fn htod_u64(&self, v: &[u64]) -> Result<CudaSlice<u64>, Box<dyn std::error::Error>> {
8231 Ok(self.gpu.stream().clone_htod(v)?)
8232 }
8233 pub fn dtoh_view(
8235 &self,
8236 d: &cudarc::driver::CudaView<f32>,
8237 ) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8238 let v = self.gpu.stream().clone_dtoh(d)?;
8239 self.gpu.stream().synchronize()?;
8240 Ok(v)
8241 }
8242 pub fn dtoh(&self, d: &CudaSlice<f32>) -> Result<Vec<f32>, Box<dyn std::error::Error>> {
8243 let v = self.gpu.stream().clone_dtoh(d)?;
8244 self.gpu.stream().synchronize()?;
8245 Ok(v)
8246 }
8247 pub fn dtoh_pair(
8251 &self,
8252 a: &CudaSlice<f32>,
8253 b: &CudaSlice<f32>,
8254 ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
8255 let av = self.gpu.stream().clone_dtoh(a)?;
8256 let bv = self.gpu.stream().clone_dtoh(b)?;
8257 self.gpu.stream().synchronize()?;
8258 Ok((av, bv))
8259 }
8260 pub fn dtoh_pair_views(
8263 &self,
8264 a: &cudarc::driver::CudaView<f32>,
8265 b: &cudarc::driver::CudaView<f32>,
8266 ) -> Result<(Vec<f32>, Vec<f32>), Box<dyn std::error::Error>> {
8267 let av = self.gpu.stream().clone_dtoh(a)?;
8268 let bv = self.gpu.stream().clone_dtoh(b)?;
8269 self.gpu.stream().synchronize()?;
8270 Ok((av, bv))
8271 }
8272 pub fn dtoh_i32(&self, d: &CudaSlice<i32>) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
8274 let v = self.gpu.stream().clone_dtoh(d)?;
8275 self.gpu.stream().synchronize()?;
8276 Ok(v)
8277 }
8278 pub fn dtoh_u8(&self, d: &CudaSlice<u8>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
8280 let v = self.gpu.stream().clone_dtoh(d)?;
8281 self.gpu.stream().synchronize()?;
8282 Ok(v)
8283 }
8284 pub fn dtoh_u8_view(
8285 &self,
8286 d: &cudarc::driver::CudaView<u8>,
8287 ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
8288 let v = self.gpu.stream().clone_dtoh(d)?;
8289 self.gpu.stream().synchronize()?;
8290 Ok(v)
8291 }
8292 pub fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8293 let s = self.gpu.stream().alloc_zeros::<f32>(n)?;
8294 self.keep_if_capturing(&s);
8295 Ok(s)
8296 }
8297
8298 pub fn prob_of_token_device(
8307 &self,
8308 logits: &CudaSlice<f32>,
8309 tok: &CudaSlice<u32>,
8310 n_vocab: usize,
8311 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8312 let nb = ARGMAX_NB;
8313 let mut part = self.alloc_uninit::<f32>(nb)?;
8314 let mut p = self.alloc_uninit::<f32>(1)?;
8315 let f1 = self.func("prob_of_token_partial_f32");
8316 let cfg1 = LaunchConfig {
8317 grid_dim: (nb as u32, 1, 1),
8318 block_dim: (256, 1, 1),
8319 shared_mem_bytes: 0,
8320 };
8321 let nv = n_vocab as i32;
8322 let __s_b1 = self.gpu.stream();
8323 let mut b1 = __s_b1.launch_builder(&f1);
8324 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
8325 unsafe {
8326 b1.launch(cfg1)?;
8327 }
8328 let f2 = self.func("prob_of_token_final_f32");
8329 let cfg2 = LaunchConfig {
8330 grid_dim: (1, 1, 1),
8331 block_dim: (256, 1, 1),
8332 shared_mem_bytes: 0,
8333 };
8334 let nbi = nb as i32;
8335 let __s_b2 = self.gpu.stream();
8336 let mut b2 = __s_b2.launch_builder(&f2);
8337 b2.arg(&part).arg(&mut p).arg(&nbi);
8338 unsafe {
8339 b2.launch(cfg2)?;
8340 }
8341 Ok(p)
8342 }
8343
8344 pub fn prob_of_token_device_col(
8351 &self,
8352 logits: &CudaSlice<f32>,
8353 tok_all: &CudaSlice<u32>,
8354 tok_idx: usize,
8355 p_out: &mut CudaSlice<f32>,
8356 p_idx: usize,
8357 n_vocab: usize,
8358 ) -> Result<(), Box<dyn std::error::Error>> {
8359 let tok_v = tok_all.slice(tok_idx..tok_idx + 1);
8360 let mut p_v = p_out.slice_mut(p_idx..p_idx + 1);
8361 let nb = ARGMAX_NB;
8362 let mut part = self.alloc_uninit::<f32>(nb)?;
8363 let f1 = self.func("prob_of_token_partial_f32");
8364 let cfg1 = LaunchConfig {
8365 grid_dim: (nb as u32, 1, 1),
8366 block_dim: (256, 1, 1),
8367 shared_mem_bytes: 0,
8368 };
8369 let nv = n_vocab as i32;
8370 let __s_b1 = self.gpu.stream();
8371 let mut b1 = __s_b1.launch_builder(&f1);
8372 b1.arg(logits).arg(&tok_v).arg(&mut part).arg(&nv);
8373 unsafe {
8374 b1.launch(cfg1)?;
8375 }
8376 let f2 = self.func("prob_of_token_final_f32");
8377 let cfg2 = LaunchConfig {
8378 grid_dim: (1, 1, 1),
8379 block_dim: (256, 1, 1),
8380 shared_mem_bytes: 0,
8381 };
8382 let nbi = nb as i32;
8383 let __s_b2 = self.gpu.stream();
8384 let mut b2 = __s_b2.launch_builder(&f2);
8385 b2.arg(&part).arg(&mut p_v).arg(&nbi);
8386 unsafe {
8387 b2.launch(cfg2)?;
8388 }
8389 Ok(())
8390 }
8391
8392 pub fn prob_of_token_device_into(
8393 &self,
8394 logits: &CudaSlice<f32>,
8395 tok: &CudaSlice<u32>,
8396 p_out: &mut CudaSlice<f32>,
8397 n_vocab: usize,
8398 ) -> Result<(), Box<dyn std::error::Error>> {
8399 let nb = ARGMAX_NB;
8400 let mut part = self.alloc_uninit::<f32>(nb)?;
8401 let f1 = self.func("prob_of_token_partial_f32");
8402 let cfg1 = LaunchConfig {
8403 grid_dim: (nb as u32, 1, 1),
8404 block_dim: (256, 1, 1),
8405 shared_mem_bytes: 0,
8406 };
8407 let nv = n_vocab as i32;
8408 let __s_b1 = self.gpu.stream();
8409 let mut b1 = __s_b1.launch_builder(&f1);
8410 b1.arg(logits).arg(tok).arg(&mut part).arg(&nv);
8411 unsafe {
8412 b1.launch(cfg1)?;
8413 }
8414 let f2 = self.func("prob_of_token_final_f32");
8415 let cfg2 = LaunchConfig {
8416 grid_dim: (1, 1, 1),
8417 block_dim: (256, 1, 1),
8418 shared_mem_bytes: 0,
8419 };
8420 let nbi = nb as i32;
8421 let __s_b2 = self.gpu.stream();
8422 let mut b2 = __s_b2.launch_builder(&f2);
8423 b2.arg(&part).arg(p_out).arg(&nbi);
8424 unsafe {
8425 b2.launch(cfg2)?;
8426 }
8427 Ok(())
8428 }
8429
8430 pub fn u32_hist_append(
8433 &self,
8434 tok: &CudaSlice<u32>,
8435 hist: &mut CudaSlice<u32>,
8436 idx: &mut CudaSlice<i32>,
8437 ) -> Result<(), Box<dyn std::error::Error>> {
8438 let f = self.func("u32_hist_append");
8439 let cfg = LaunchConfig {
8440 grid_dim: (1, 1, 1),
8441 block_dim: (32, 1, 1),
8442 shared_mem_bytes: 0,
8443 };
8444 let __s_b = self.gpu.stream();
8445 let mut b = __s_b.launch_builder(&f);
8446 b.arg(tok).arg(&mut *hist).arg(&mut *idx);
8447 unsafe {
8448 b.launch(cfg)?;
8449 }
8450 Ok(())
8451 }
8452
8453 pub fn argmax_token_device(
8454 &self,
8455 logits: &CudaSlice<f32>,
8456 n_vocab: usize,
8457 ) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
8458 let mut tok = unsafe { self.gpu.stream().alloc::<u32>(1)? };
8459 self.argmax_token_device_into(logits, &mut tok, n_vocab)?;
8460 Ok(tok)
8461 }
8462 pub fn argmax_token_device_into(
8469 &self,
8470 logits: &CudaSlice<f32>,
8471 tok: &mut CudaSlice<u32>,
8472 n_vocab: usize,
8473 ) -> Result<(), Box<dyn std::error::Error>> {
8474 let nb = ARGMAX_NB;
8475 let f1 = self.func("argmax_partial_f32");
8476 let f2 = self.func("argmax_final_f32");
8477 let mut guard = self.argmax_partials.lock().unwrap();
8478 if guard.is_none() {
8479 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
8482 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
8483 *guard = Some((pv, pi));
8484 }
8485 let (part_v, part_i) = guard.as_mut().unwrap();
8486 let nv = n_vocab as i32;
8487 let nbi = nb as i32;
8488 let cfg1 = LaunchConfig {
8490 grid_dim: (nb as u32, 1, 1),
8491 block_dim: (256, 1, 1),
8492 shared_mem_bytes: 0,
8493 };
8494 let __s_b1 = self.gpu.stream();
8495 let mut b1 = __s_b1.launch_builder(&f1);
8496 b1.arg(logits).arg(&mut *part_v).arg(&mut *part_i).arg(&nv);
8497 unsafe {
8498 b1.launch(cfg1)?;
8499 }
8500 let cfg2 = LaunchConfig {
8502 grid_dim: (1, 1, 1),
8503 block_dim: (256, 1, 1),
8504 shared_mem_bytes: 0,
8505 };
8506 let __s_b2 = self.gpu.stream();
8507 let mut b2 = __s_b2.launch_builder(&f2);
8508 b2.arg(&*part_v).arg(&*part_i).arg(tok).arg(&nbi);
8509 unsafe {
8510 b2.launch(cfg2)?;
8511 }
8512 Ok(())
8513 }
8514 pub fn argmax_token_device_col(
8520 &self,
8521 logits: &CudaSlice<f32>,
8522 col: usize,
8523 n_vocab: usize,
8524 toks: &mut CudaSlice<u32>,
8525 out_idx: usize,
8526 ) -> Result<(), Box<dyn std::error::Error>> {
8527 let nb = ARGMAX_NB;
8528 let f1 = self.func("argmax_partial_f32");
8529 let f2 = self.func("argmax_final_f32");
8530 let mut guard = self.argmax_partials.lock().unwrap();
8531 if guard.is_none() {
8532 let pv = self.gpu.stream().alloc_zeros::<f32>(nb)?;
8533 let pi = self.gpu.stream().alloc_zeros::<i32>(nb)?;
8534 *guard = Some((pv, pi));
8535 }
8536 let (part_v, part_i) = guard.as_mut().unwrap();
8537 let col_view = logits.slice(col * n_vocab..(col + 1) * n_vocab);
8538 let nv = n_vocab as i32;
8539 let nbi = nb as i32;
8540 let cfg1 = LaunchConfig {
8541 grid_dim: (nb as u32, 1, 1),
8542 block_dim: (256, 1, 1),
8543 shared_mem_bytes: 0,
8544 };
8545 let __s_b1 = self.gpu.stream();
8546 let mut b1 = __s_b1.launch_builder(&f1);
8547 b1.arg(&col_view)
8548 .arg(&mut *part_v)
8549 .arg(&mut *part_i)
8550 .arg(&nv);
8551 unsafe {
8552 b1.launch(cfg1)?;
8553 }
8554 let mut tok_view = toks.slice_mut(out_idx..out_idx + 1);
8555 let cfg2 = LaunchConfig {
8556 grid_dim: (1, 1, 1),
8557 block_dim: (256, 1, 1),
8558 shared_mem_bytes: 0,
8559 };
8560 let __s_b2 = self.gpu.stream();
8561 let mut b2 = __s_b2.launch_builder(&f2);
8562 b2.arg(&*part_v).arg(&*part_i).arg(&mut tok_view).arg(&nbi);
8563 unsafe {
8564 b2.launch(cfg2)?;
8565 }
8566 Ok(())
8567 }
8568 pub fn htod_u32_v(&self, v: &[u32]) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
8570 Ok(self.gpu.stream().clone_htod(v)?)
8571 }
8572 pub fn dtoh_u64(&self, d: &CudaSlice<u64>) -> Result<Vec<u64>, Box<dyn std::error::Error>> {
8573 let v = self.gpu.stream().clone_dtoh(d)?;
8574 self.gpu.stream().synchronize()?;
8575 Ok(v)
8576 }
8577
8578 pub fn dtoh_u32(&self, d: &CudaSlice<u32>) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
8579 let v = self.gpu.stream().clone_dtoh(d)?;
8580 self.gpu.stream().synchronize()?;
8581 Ok(v)
8582 }
8583 pub fn htod_u32_into(
8587 &self,
8588 dst: &mut CudaSlice<u32>,
8589 src: &[u32],
8590 ) -> Result<(), Box<dyn std::error::Error>> {
8591 let mut view = dst.slice_mut(0..src.len());
8592 self.gpu.stream().memcpy_htod(src, &mut view)?;
8593 Ok(())
8594 }
8595
8596 pub fn htod_i32_into(
8599 &self,
8600 dst: &mut CudaSlice<i32>,
8601 src: &[i32],
8602 ) -> Result<(), Box<dyn std::error::Error>> {
8603 let mut view = dst.slice_mut(0..src.len());
8604 self.gpu.stream().memcpy_htod(src, &mut view)?;
8605 Ok(())
8606 }
8607
8608 pub fn alloc_u32_zeroed(&self, n: usize) -> Result<CudaSlice<u32>, Box<dyn std::error::Error>> {
8609 let s = self.gpu.stream().alloc_zeros::<u32>(n)?;
8610 self.keep_if_capturing(&s);
8611 Ok(s)
8612 }
8613 pub fn embed_gather_device_into(
8616 &self,
8617 embd: &CudaSlice<u8>,
8618 token_d: &CudaSlice<u32>,
8619 x_out: &mut CudaSlice<f32>,
8620 n_embd: usize,
8621 qtype: i32,
8622 row_bytes: usize,
8623 ) -> Result<(), Box<dyn std::error::Error>> {
8624 let f = self.func("embed_gather_u32");
8625 let cfg = LaunchConfig {
8626 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
8627 block_dim: (256, 1, 1),
8628 shared_mem_bytes: 0,
8629 };
8630 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
8631 let __s_b = self.gpu.stream();
8632 let mut b = __s_b.launch_builder(&f);
8633 b.arg(embd)
8634 .arg(token_d)
8635 .arg(x_out)
8636 .arg(&ne)
8637 .arg(&qt)
8638 .arg(&rb);
8639 unsafe {
8640 b.launch(cfg)?;
8641 }
8642 Ok(())
8643 }
8644 pub fn dtoh_i32_one(&self, d: &CudaSlice<i32>) -> Result<i32, Box<dyn std::error::Error>> {
8646 let v = self.gpu.stream().clone_dtoh(d)?;
8647 self.gpu.stream().synchronize()?;
8648 Ok(v[0])
8649 }
8650 pub fn i32_set_k(
8657 &self,
8658 dst: &mut CudaSlice<i32>,
8659 v: i32,
8660 ) -> Result<(), Box<dyn std::error::Error>> {
8661 let f = self.func("i32_set_k");
8662 let cfg = LaunchConfig {
8663 grid_dim: (1, 1, 1),
8664 block_dim: (1, 1, 1),
8665 shared_mem_bytes: 0,
8666 };
8667 let idx = 0i32;
8668 let __s_b = self.gpu.stream();
8669 let mut b = __s_b.launch_builder(&f);
8670 b.arg(dst).arg(&v).arg(&idx);
8671 unsafe {
8672 b.launch(cfg)?;
8673 }
8674 Ok(())
8675 }
8676
8677 pub fn set_i32_one(
8678 &self,
8679 d: &mut CudaSlice<i32>,
8680 v: i32,
8681 ) -> Result<(), Box<dyn std::error::Error>> {
8682 self.gpu.stream().memcpy_htod(&[v], d)?;
8683 Ok(())
8684 }
8685 pub fn set_u32_one(
8688 &self,
8689 d: &mut CudaSlice<u32>,
8690 v: u32,
8691 ) -> Result<(), Box<dyn std::error::Error>> {
8692 self.gpu.stream().memcpy_htod(&[v], d)?;
8693 Ok(())
8694 }
8695 pub fn dtoh_u32_one(&self, d: &CudaSlice<u32>) -> Result<u32, Box<dyn std::error::Error>> {
8697 let v = self.gpu.stream().clone_dtoh(d)?;
8698 self.gpu.stream().synchronize()?;
8699 Ok(v[0])
8700 }
8701 pub fn upload_u8(&self, bytes: &[u8]) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
8703 Ok(self.gpu.stream().clone_htod(bytes)?)
8704 }
8705 pub fn embed_gather_device(
8709 &self,
8710 embd: &CudaSlice<u8>,
8711 token_d: &CudaSlice<u32>,
8712 n_embd: usize,
8713 qtype: i32,
8714 row_bytes: usize,
8715 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8716 let f = self.func("embed_gather_u32");
8717 let mut x = self.alloc_uninit::<f32>(n_embd)?;
8718 let cfg = LaunchConfig {
8719 grid_dim: (((n_embd as u32 + 255) / 256).max(1), 1, 1),
8720 block_dim: (256, 1, 1),
8721 shared_mem_bytes: 0,
8722 };
8723 let (ne, qt, rb) = (n_embd as i32, qtype, row_bytes as i64);
8724 let __s_b = self.gpu.stream();
8725 let mut b = __s_b.launch_builder(&f);
8726 b.arg(embd)
8727 .arg(token_d)
8728 .arg(&mut x)
8729 .arg(&ne)
8730 .arg(&qt)
8731 .arg(&rb);
8732 unsafe {
8733 b.launch(cfg)?;
8734 }
8735 Ok(x)
8736 }
8737
8738 pub fn embed_gather_device_t(
8742 &self,
8743 embd: &CudaSlice<u8>,
8744 tokens: &[u32],
8745 n_embd: usize,
8746 qtype: i32,
8747 row_bytes: usize,
8748 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8749 let t = tokens.len();
8750 let tok_d = self.gpu.stream().clone_htod(tokens)?;
8751 let f = self.func("embed_gather_u32_t");
8752 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
8753 let cfg = LaunchConfig {
8754 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
8755 block_dim: (256, 1, 1),
8756 shared_mem_bytes: 0,
8757 };
8758 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
8759 let __s_b = self.gpu.stream();
8760 let mut b = __s_b.launch_builder(&f);
8761 b.arg(embd)
8762 .arg(&tok_d)
8763 .arg(&mut x)
8764 .arg(&ne)
8765 .arg(&qt)
8766 .arg(&rb)
8767 .arg(&ti);
8768 unsafe {
8769 b.launch(cfg)?;
8770 }
8771 Ok(x)
8772 }
8773
8774 pub fn embed_gather_device_tv(
8779 &self,
8780 embd: &CudaSlice<u8>,
8781 tok_v: &cudarc::driver::CudaView<u32>,
8782 t: usize,
8783 n_embd: usize,
8784 qtype: i32,
8785 row_bytes: usize,
8786 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8787 let f = self.func("embed_gather_u32_t");
8788 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
8789 let cfg = LaunchConfig {
8790 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
8791 block_dim: (256, 1, 1),
8792 shared_mem_bytes: 0,
8793 };
8794 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
8795 let __s_b = self.gpu.stream();
8796 let mut b = __s_b.launch_builder(&f);
8797 b.arg(embd)
8798 .arg(tok_v)
8799 .arg(&mut x)
8800 .arg(&ne)
8801 .arg(&qt)
8802 .arg(&rb)
8803 .arg(&ti);
8804 unsafe {
8805 b.launch(cfg)?;
8806 }
8807 Ok(x)
8808 }
8809
8810 pub fn embed_gather_device_td(
8811 &self,
8812 embd: &CudaSlice<u8>,
8813 tok_d: &CudaSlice<u32>,
8814 t: usize,
8815 n_embd: usize,
8816 qtype: i32,
8817 row_bytes: usize,
8818 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8819 let f = self.func("embed_gather_u32_t");
8820 let mut x = self.alloc_uninit::<f32>(t * n_embd)?;
8821 let cfg = LaunchConfig {
8822 grid_dim: (((n_embd as u32 + 255) / 256).max(1), t as u32, 1),
8823 block_dim: (256, 1, 1),
8824 shared_mem_bytes: 0,
8825 };
8826 let (ne, qt, rb, ti) = (n_embd as i32, qtype, row_bytes as i64, t as i32);
8827 let __s_b = self.gpu.stream();
8828 let mut b = __s_b.launch_builder(&f);
8829 b.arg(embd)
8830 .arg(tok_d)
8831 .arg(&mut x)
8832 .arg(&ne)
8833 .arg(&qt)
8834 .arg(&rb)
8835 .arg(&ti);
8836 unsafe {
8837 b.launch(cfg)?;
8838 }
8839 Ok(x)
8840 }
8841
8842 #[inline]
8848 fn keep_if_capturing<T: cudarc::driver::DeviceRepr + Send + 'static>(&self, s: &CudaSlice<T>) {
8850 if self
8851 .capture_keep_on
8852 .load(std::sync::atomic::Ordering::Relaxed)
8853 {
8854 self.capture_keep.lock().unwrap().push(Box::new(s.clone()));
8855 }
8856 }
8857
8858 fn alloc_uninit<T: cudarc::driver::DeviceRepr + Send + 'static>(
8859 &self,
8860 n: usize,
8861 ) -> Result<CudaSlice<T>, Box<dyn std::error::Error>> {
8862 let mut s = unsafe { self.gpu.stream().alloc::<T>(n)? };
8863 {
8867 static Z: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8868 if *Z.get_or_init(|| std::env::var("MEMRA_DEBUG_ZERO_ALLOCS").as_deref() == Ok("1")) {
8869 use cudarc::driver::DevicePtrMut;
8871 let n_bytes = s.len() * std::mem::size_of::<T>();
8872 let stream = self.gpu.stream();
8873 let (p_, _g) = s.device_ptr_mut(&stream);
8874 unsafe {
8875 cudarc::driver::sys::cuMemsetD8Async(p_, 0, n_bytes, stream.cu_stream())
8876 .result()?;
8877 }
8878 }
8879 }
8880 self.keep_if_capturing(&s);
8881 Ok(s)
8882 }
8883
8884 pub fn uninit_q8_pair(
8889 &self,
8890 n: usize,
8891 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
8892 Ok((
8893 self.alloc_uninit::<i8>(n)?,
8894 self.alloc_uninit::<f32>(n / 32)?,
8895 ))
8896 }
8897
8898 pub fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
8899 self.alloc_uninit::<f32>(n)
8900 }
8901
8902 pub fn alloc_i8_uninit(&self, n: usize) -> Result<CudaSlice<i8>, Box<dyn std::error::Error>> {
8904 self.alloc_uninit::<i8>(n)
8905 }
8906
8907 #[allow(clippy::too_many_arguments)]
8911 pub fn rms_norm3(
8912 &self,
8913 x: &CudaSlice<f32>,
8914 w0: &CudaSlice<f32>,
8915 w1: &CudaSlice<f32>,
8916 w2: &CudaSlice<f32>,
8917 d0: &mut CudaSlice<f32>,
8918 d1: &mut CudaSlice<f32>,
8919 d2: &mut CudaSlice<f32>,
8920 ncols: usize,
8921 nrows: usize,
8922 eps: f32,
8923 ) -> Result<(), Box<dyn std::error::Error>> {
8924 let f = self.func("rms_norm3_f32");
8925 let cfg = LaunchConfig {
8926 grid_dim: (nrows as u32, 1, 1),
8927 block_dim: (rms_block(), 1, 1),
8928 shared_mem_bytes: 0,
8929 };
8930 let (nc, e) = (ncols as i32, eps);
8931 let __s_b = self.gpu.stream();
8932 let mut b = __s_b.launch_builder(&f);
8933 b.arg(x)
8934 .arg(w0)
8935 .arg(w1)
8936 .arg(w2)
8937 .arg(d0)
8938 .arg(d1)
8939 .arg(d2)
8940 .arg(&nc)
8941 .arg(&e);
8942 unsafe {
8943 b.launch(cfg)?;
8944 }
8945 Ok(())
8946 }
8947
8948 #[allow(clippy::too_many_arguments)]
8950 pub fn qkvnorm_w_on_prefill(rows: usize, ncols: usize) -> bool {
8953 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
8954 *WARP_ON.get_or_init(|| {
8955 std::env::var("MEMRA_QKVNORM_W")
8956 .map(|v| v != "0")
8957 .unwrap_or(true)
8958 }) && ncols % 4 == 0
8959 && rows >= 64
8960 }
8961
8962 #[allow(clippy::too_many_arguments)]
8965 pub fn rms_norm_qkv_w4b(
8966 &self,
8967 q: &CudaSlice<f32>,
8968 k: &CudaSlice<f32>,
8969 v: &CudaSlice<f32>,
8970 wq: &CudaSlice<f32>,
8971 wk: &CudaSlice<f32>,
8972 wv: &CudaSlice<f32>,
8973 dq: &mut CudaSlice<f32>,
8974 dk: &mut CudaSlice<f32>,
8975 dv: &mut CudaSlice<f32>,
8976 dvb: &mut CudaSlice<u8>,
8977 ncols: usize,
8978 rq: usize,
8979 rk: usize,
8980 eps: f32,
8981 vf16: bool,
8982 ) -> Result<(), Box<dyn std::error::Error>> {
8983 assert!(ncols % 4 == 0 && rq + 2 * rk >= 64);
8984 let f = self.func("rms_norm_qkv_w4b_f32");
8985 let rows = (rq + 2 * rk) as u32;
8986 let cfg = LaunchConfig {
8987 grid_dim: (rows.div_ceil(8), 1, 1),
8988 block_dim: (256, 1, 1),
8989 shared_mem_bytes: 0,
8990 };
8991 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
8992 let vf = vf16 as i32;
8993 let __s_b = self.gpu.stream();
8994 let mut b = __s_b.launch_builder(&f);
8995 b.arg(q)
8996 .arg(k)
8997 .arg(v)
8998 .arg(wq)
8999 .arg(wk)
9000 .arg(wv)
9001 .arg(dq)
9002 .arg(dk)
9003 .arg(dv)
9004 .arg(&mut *dvb)
9005 .arg(&nc)
9006 .arg(&rqi)
9007 .arg(&rki)
9008 .arg(&rvi)
9009 .arg(&e)
9010 .arg(&vf);
9011 unsafe {
9012 b.launch(cfg)?;
9013 }
9014 Ok(())
9015 }
9016
9017 pub fn rms_norm_qkv(
9018 &self,
9019 q: &CudaSlice<f32>,
9020 k: &CudaSlice<f32>,
9021 v: &CudaSlice<f32>,
9022 wq: &CudaSlice<f32>,
9023 wk: &CudaSlice<f32>,
9024 wv: &CudaSlice<f32>,
9025 dq: &mut CudaSlice<f32>,
9026 dk: &mut CudaSlice<f32>,
9027 dv: &mut CudaSlice<f32>,
9028 ncols: usize,
9029 rq: usize,
9030 rk: usize,
9031 eps: f32,
9032 ) -> Result<(), Box<dyn std::error::Error>> {
9033 static WARP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
9037 let warp_on = *WARP_ON.get_or_init(|| {
9038 std::env::var("MEMRA_QKVNORM_W")
9039 .map(|v| v != "0")
9040 .unwrap_or(true)
9041 });
9042 if warp_on && ncols % 4 == 0 && rq + 2 * rk >= 64 {
9045 let f = self.func("rms_norm_qkv_w4_f32");
9046 let rows = (rq + 2 * rk) as u32;
9047 let cfg = LaunchConfig {
9048 grid_dim: (rows.div_ceil(8), 1, 1),
9049 block_dim: (256, 1, 1),
9050 shared_mem_bytes: 0,
9051 };
9052 let (nc, rqi, rki, rvi, e) = (ncols as i32, rq as i32, rk as i32, rk as i32, eps);
9053 let __s_b = self.gpu.stream();
9054 let mut b = __s_b.launch_builder(&f);
9055 b.arg(q)
9056 .arg(k)
9057 .arg(v)
9058 .arg(wq)
9059 .arg(wk)
9060 .arg(wv)
9061 .arg(dq)
9062 .arg(dk)
9063 .arg(dv)
9064 .arg(&nc)
9065 .arg(&rqi)
9066 .arg(&rki)
9067 .arg(&rvi)
9068 .arg(&e);
9069 unsafe {
9070 b.launch(cfg)?;
9071 }
9072 return Ok(());
9073 }
9074 let f = self.func("rms_norm_qkv_f32");
9075 let grid = (rq + 2 * rk) as u32;
9076 let cfg = LaunchConfig {
9077 grid_dim: (grid, 1, 1),
9078 block_dim: (rms_block(), 1, 1),
9079 shared_mem_bytes: 0,
9080 };
9081 let (nc, rqi, rki, e) = (ncols as i32, rq as i32, rk as i32, eps);
9082 let __s_b = self.gpu.stream();
9083 let mut b = __s_b.launch_builder(&f);
9084 b.arg(q)
9085 .arg(k)
9086 .arg(v)
9087 .arg(wq)
9088 .arg(wk)
9089 .arg(wv)
9090 .arg(dq)
9091 .arg(dk)
9092 .arg(dv)
9093 .arg(&nc)
9094 .arg(&rqi)
9095 .arg(&rki)
9096 .arg(&e);
9097 unsafe {
9098 b.launch(cfg)?;
9099 }
9100 Ok(())
9101 }
9102
9103 #[allow(clippy::too_many_arguments)]
9105 pub fn rms_norm2x(
9106 &self,
9107 a: &CudaSlice<f32>,
9108 bb: &CudaSlice<f32>,
9109 wa: &CudaSlice<f32>,
9110 wb: &CudaSlice<f32>,
9111 da: &mut CudaSlice<f32>,
9112 db: &mut CudaSlice<f32>,
9113 ncols: usize,
9114 nrows: usize,
9115 eps: f32,
9116 ) -> Result<(), Box<dyn std::error::Error>> {
9117 let f = self.func("rms_norm2x_f32");
9118 let cfg = LaunchConfig {
9119 grid_dim: (2 * nrows as u32, 1, 1),
9120 block_dim: (rms_block(), 1, 1),
9121 shared_mem_bytes: 0,
9122 };
9123 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
9124 let __s_b = self.gpu.stream();
9125 let mut b = __s_b.launch_builder(&f);
9126 b.arg(a)
9127 .arg(bb)
9128 .arg(wa)
9129 .arg(wb)
9130 .arg(da)
9131 .arg(db)
9132 .arg(&nc)
9133 .arg(&nr)
9134 .arg(&e);
9135 unsafe {
9136 b.launch(cfg)?;
9137 }
9138 Ok(())
9139 }
9140
9141 pub fn softcap(
9143 &self,
9144 y: &mut CudaSlice<f32>,
9145 cap: f32,
9146 n: usize,
9147 ) -> Result<(), Box<dyn std::error::Error>> {
9148 let f = self.func("softcap_f32");
9149 let cfg = LaunchConfig::for_num_elems(n as u32);
9150 let ni = n as i32;
9151 let __s_b = self.gpu.stream();
9152 let mut b = __s_b.launch_builder(&f);
9153 b.arg(y).arg(&cap).arg(&ni);
9154 unsafe {
9155 b.launch(cfg)?;
9156 }
9157 Ok(())
9158 }
9159
9160 pub fn mask_ids_rows(
9163 &self,
9164 y: &mut CudaSlice<f32>,
9165 ids: &CudaSlice<i32>,
9166 n_ids: usize,
9167 n_vocab: usize,
9168 t: usize,
9169 ) -> Result<(), Box<dyn std::error::Error>> {
9170 let f = self.func("mask_ids_rows_f32");
9171 let cfg = LaunchConfig::for_num_elems((n_ids * t) as u32);
9172 let (ni, nv, ti) = (n_ids as i32, n_vocab as i32, t as i32);
9173 let __s_b = self.gpu.stream();
9174 let mut b = __s_b.launch_builder(&f);
9175 b.arg(y).arg(ids).arg(&ni).arg(&nv).arg(&ti);
9176 unsafe {
9177 b.launch(cfg)?;
9178 }
9179 Ok(())
9180 }
9181
9182 #[allow(clippy::too_many_arguments)]
9184 pub fn add_scale_rms_norm(
9185 &self,
9186 a: &CudaSlice<f32>,
9187 b_in: &CudaSlice<f32>,
9188 c: f32,
9189 w: &CudaSlice<f32>,
9190 res: &mut CudaSlice<f32>,
9191 dst: &mut CudaSlice<f32>,
9192 ncols: usize,
9193 nrows: usize,
9194 eps: f32,
9195 ) -> Result<(), Box<dyn std::error::Error>> {
9196 let f = self.func("add_scale_rms_norm_f32");
9197 let cfg = LaunchConfig {
9198 grid_dim: (nrows as u32, 1, 1),
9199 block_dim: (rms_block(), 1, 1),
9200 shared_mem_bytes: 0,
9201 };
9202 let (nc, e2) = (ncols as i32, eps);
9203 let __s_b = self.gpu.stream();
9204 let mut b = __s_b.launch_builder(&f);
9205 b.arg(a)
9206 .arg(b_in)
9207 .arg(&c)
9208 .arg(w)
9209 .arg(res)
9210 .arg(dst)
9211 .arg(&nc)
9212 .arg(&e2);
9213 unsafe {
9214 b.launch(cfg)?;
9215 }
9216 Ok(())
9217 }
9218
9219 #[allow(clippy::too_many_arguments)]
9222 pub fn add_scale_rms_norm_q8_1(
9223 &self,
9224 a: &CudaSlice<f32>,
9225 b_in: &CudaSlice<f32>,
9226 c: f32,
9227 w: &CudaSlice<f32>,
9228 res: &mut CudaSlice<f32>,
9229 ncols: usize,
9230 nrows: usize,
9231 eps: f32,
9232 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9233 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9234 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9235 let (nc, e2) = (ncols as i32, eps);
9236 if Self::pdl_on() && Self::pdl_wb_on() {
9237 {
9238 use cudarc::driver::{DevicePtr, DevicePtrMut};
9239 let s = &self.gpu.stream();
9240 let (pa, _g0) = a.device_ptr(s);
9241 let (pb, _g1) = b_in.device_ptr(s);
9242 let (pw, _g2) = w.device_ptr(s);
9243 let (pr, _g3) = res.device_ptr_mut(s);
9244 let (pq, _g4) = out_q.device_ptr_mut(s);
9245 let (pd, _g5) = out_d.device_ptr_mut(s);
9246 let mut ps = [
9247 &pa as *const _ as *mut std::ffi::c_void,
9248 &pb as *const _ as *mut _,
9249 &c as *const _ as *mut _,
9250 &pw as *const _ as *mut _,
9251 &pr as *const _ as *mut _,
9252 &pq as *const _ as *mut _,
9253 &pd as *const _ as *mut _,
9254 &nc as *const _ as *mut _,
9255 &e2 as *const _ as *mut _,
9256 ];
9257 unsafe {
9258 self.launch_pdl(
9259 "add_scale_rms_norm_q8_1",
9260 (nrows as u32, 1, 1),
9261 (rms_block(), 1, 1),
9262 &mut ps,
9263 )?;
9264 }
9265 }
9266 return Ok((out_q, out_d));
9267 }
9268 let f = self.func("add_scale_rms_norm_q8_1");
9269 let cfg = LaunchConfig {
9270 grid_dim: (nrows as u32, 1, 1),
9271 block_dim: (rms_block(), 1, 1),
9272 shared_mem_bytes: 0,
9273 };
9274 let __s_b = self.gpu.stream();
9275 let mut b = __s_b.launch_builder(&f);
9276 b.arg(a)
9277 .arg(b_in)
9278 .arg(&c)
9279 .arg(w)
9280 .arg(res)
9281 .arg(&mut out_q)
9282 .arg(&mut out_d)
9283 .arg(&nc)
9284 .arg(&e2);
9285 unsafe {
9286 b.launch(cfg)?;
9287 }
9288 Ok((out_q, out_d))
9289 }
9290
9291 #[allow(clippy::too_many_arguments)]
9293 pub fn add_scale_rms_norm_q8_1_into(
9294 &self,
9295 a: &CudaSlice<f32>,
9296 b_in: &CudaSlice<f32>,
9297 c: f32,
9298 w: &CudaSlice<f32>,
9299 res: &mut CudaSlice<f32>,
9300 ncols: usize,
9301 nrows: usize,
9302 eps: f32,
9303 out_q: &mut CudaSlice<i8>,
9304 out_d: &mut CudaSlice<f32>,
9305 ) -> Result<(), Box<dyn std::error::Error>> {
9306 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
9307 let (nc, e2) = (ncols as i32, eps);
9308 if Self::pdl_on() && Self::pdl_wb_on() {
9309 use cudarc::driver::{DevicePtr, DevicePtrMut};
9310 let s = &self.gpu.stream();
9311 let (pa, _g0) = a.device_ptr(s);
9312 let (pb, _g1) = b_in.device_ptr(s);
9313 let (pw, _g2) = w.device_ptr(s);
9314 let (pr, _g3) = res.device_ptr_mut(s);
9315 let (pq, _g4) = out_q.device_ptr_mut(s);
9316 let (pd, _g5) = out_d.device_ptr_mut(s);
9317 let mut ps = [
9318 &pa as *const _ as *mut std::ffi::c_void,
9319 &pb as *const _ as *mut _,
9320 &c as *const _ as *mut _,
9321 &pw as *const _ as *mut _,
9322 &pr as *const _ as *mut _,
9323 &pq as *const _ as *mut _,
9324 &pd as *const _ as *mut _,
9325 &nc as *const _ as *mut _,
9326 &e2 as *const _ as *mut _,
9327 ];
9328 unsafe {
9329 self.launch_pdl(
9330 "add_scale_rms_norm_q8_1",
9331 (nrows as u32, 1, 1),
9332 (rms_block(), 1, 1),
9333 &mut ps,
9334 )?;
9335 }
9336 return Ok(());
9337 }
9338 let f = self.func("add_scale_rms_norm_q8_1");
9339 let cfg = LaunchConfig {
9340 grid_dim: (nrows as u32, 1, 1),
9341 block_dim: (rms_block(), 1, 1),
9342 shared_mem_bytes: 0,
9343 };
9344 let __s_b = self.gpu.stream();
9345 let mut b = __s_b.launch_builder(&f);
9346 b.arg(a)
9347 .arg(b_in)
9348 .arg(&c)
9349 .arg(w)
9350 .arg(res)
9351 .arg(&mut *out_q)
9352 .arg(&mut *out_d)
9353 .arg(&nc)
9354 .arg(&e2);
9355 unsafe {
9356 b.launch(cfg)?;
9357 }
9358 Ok(())
9359 }
9360
9361 #[allow(clippy::too_many_arguments)]
9364 pub fn rms_pre_add_scale_rms_norm_q8_1(
9365 &self,
9366 a: &CudaSlice<f32>,
9367 wa: &CudaSlice<f32>,
9368 b_in: &CudaSlice<f32>,
9369 c: f32,
9370 w: &CudaSlice<f32>,
9371 res: &mut CudaSlice<f32>,
9372 ncols: usize,
9373 nrows: usize,
9374 eps: f32,
9375 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9376 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9377 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9378 let (nc, e2) = (ncols as i32, eps);
9379 if Self::pdl_on() {
9380 {
9381 use cudarc::driver::{DevicePtr, DevicePtrMut};
9382 let s = &self.gpu.stream();
9383 let (pa, _g0) = a.device_ptr(s);
9384 let (pwa, _g1) = wa.device_ptr(s);
9385 let (pb, _g2) = b_in.device_ptr(s);
9386 let (pw, _g3) = w.device_ptr(s);
9387 let (pr, _g4) = res.device_ptr_mut(s);
9388 let (pq, _g5) = out_q.device_ptr_mut(s);
9389 let (pd, _g6) = out_d.device_ptr_mut(s);
9390 let mut ps = [
9391 &pa as *const _ as *mut std::ffi::c_void,
9392 &pwa as *const _ as *mut _,
9393 &pb as *const _ as *mut _,
9394 &c as *const _ as *mut _,
9395 &pw as *const _ as *mut _,
9396 &pr as *const _ as *mut _,
9397 &pq as *const _ as *mut _,
9398 &pd as *const _ as *mut _,
9399 &nc as *const _ as *mut _,
9400 &e2 as *const _ as *mut _,
9401 ];
9402 unsafe {
9403 self.launch_pdl(
9404 "rms_pre_add_scale_rms_norm_q8_1",
9405 (nrows as u32, 1, 1),
9406 (rms_block(), 1, 1),
9407 &mut ps,
9408 )?;
9409 }
9410 }
9411 return Ok((out_q, out_d));
9412 }
9413 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
9414 let cfg = LaunchConfig {
9415 grid_dim: (nrows as u32, 1, 1),
9416 block_dim: (rms_block(), 1, 1),
9417 shared_mem_bytes: 0,
9418 };
9419 let __s_b = self.gpu.stream();
9420 let mut b = __s_b.launch_builder(&f);
9421 b.arg(a)
9422 .arg(wa)
9423 .arg(b_in)
9424 .arg(&c)
9425 .arg(w)
9426 .arg(res)
9427 .arg(&mut out_q)
9428 .arg(&mut out_d)
9429 .arg(&nc)
9430 .arg(&e2);
9431 unsafe {
9432 b.launch(cfg)?;
9433 }
9434 Ok((out_q, out_d))
9435 }
9436
9437 pub fn gelu_tanh_mul_q8_1(
9440 &self,
9441 gate: &CudaSlice<f32>,
9442 up: &cudarc::driver::CudaView<f32>,
9443 act: &mut CudaSlice<f32>,
9444 ncols: usize,
9445 nrows: usize,
9446 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9447 debug_assert!(ncols % 128 == 0);
9448 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
9449 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9450 let nc = ncols as i32;
9451 if Self::pdl_on() {
9452 {
9453 use cudarc::driver::{DevicePtr, DevicePtrMut};
9454 let s = &self.gpu.stream();
9455 let (pg, _g0) = gate.device_ptr(s);
9456 let (pu, _g1) = up.device_ptr(s);
9457 let (pact, _g2) = act.device_ptr_mut(s);
9458 let (pq, _g3) = out_q.device_ptr_mut(s);
9459 let (pd, _g4) = out_d.device_ptr_mut(s);
9460 let mut ps = [
9461 &pg as *const _ as *mut std::ffi::c_void,
9462 &pu as *const _ as *mut _,
9463 &pact as *const _ as *mut _,
9464 &pq as *const _ as *mut _,
9465 &pd as *const _ as *mut _,
9466 &nc as *const _ as *mut _,
9467 ];
9468 unsafe {
9469 self.launch_pdl(
9470 "gelu_tanh_mul_q8_1",
9471 (nrows as u32, 1, 1),
9472 (rms_block(), 1, 1),
9473 &mut ps,
9474 )?;
9475 }
9476 }
9477 return Ok((out_q, out_d));
9478 }
9479 let f = self.func("gelu_tanh_mul_q8_1");
9480 let cfg = LaunchConfig {
9481 grid_dim: (nrows as u32, 1, 1),
9482 block_dim: (rms_block(), 1, 1),
9483 shared_mem_bytes: 0,
9484 };
9485 let __s_b = self.gpu.stream();
9486 let mut b = __s_b.launch_builder(&f);
9487 b.arg(gate)
9488 .arg(up)
9489 .arg(act)
9490 .arg(&mut out_q)
9491 .arg(&mut out_d)
9492 .arg(&nc);
9493 unsafe {
9494 b.launch(cfg)?;
9495 }
9496 Ok((out_q, out_d))
9497 }
9498
9499 #[allow(clippy::too_many_arguments)]
9501 pub fn gelu_tanh_mul_q8_1_into(
9502 &self,
9503 gate: &CudaSlice<f32>,
9504 up: &cudarc::driver::CudaView<f32>,
9505 act: &mut CudaSlice<f32>,
9506 ncols: usize,
9507 nrows: usize,
9508 out_q: &mut CudaSlice<i8>,
9509 out_d: &mut CudaSlice<f32>,
9510 ) -> Result<(), Box<dyn std::error::Error>> {
9511 debug_assert!(ncols % 128 == 0);
9512 debug_assert!(out_q.len() >= nrows * ncols && out_d.len() >= nrows * (ncols / 32));
9513 let nc = ncols as i32;
9514 if Self::pdl_on() {
9515 use cudarc::driver::{DevicePtr, DevicePtrMut};
9516 let s = &self.gpu.stream();
9517 let (pg, _g0) = gate.device_ptr(s);
9518 let (pu, _g1) = up.device_ptr(s);
9519 let (pact, _g2) = act.device_ptr_mut(s);
9520 let (pq, _g3) = out_q.device_ptr_mut(s);
9521 let (pd, _g4) = out_d.device_ptr_mut(s);
9522 let mut ps = [
9523 &pg as *const _ as *mut std::ffi::c_void,
9524 &pu as *const _ as *mut _,
9525 &pact as *const _ as *mut _,
9526 &pq as *const _ as *mut _,
9527 &pd as *const _ as *mut _,
9528 &nc as *const _ as *mut _,
9529 ];
9530 unsafe {
9531 self.launch_pdl(
9532 "gelu_tanh_mul_q8_1",
9533 (nrows as u32, 1, 1),
9534 (rms_block(), 1, 1),
9535 &mut ps,
9536 )?;
9537 }
9538 return Ok(());
9539 }
9540 let f = self.func("gelu_tanh_mul_q8_1");
9541 let cfg = LaunchConfig {
9542 grid_dim: (nrows as u32, 1, 1),
9543 block_dim: (rms_block(), 1, 1),
9544 shared_mem_bytes: 0,
9545 };
9546 let __s_b = self.gpu.stream();
9547 let mut b = __s_b.launch_builder(&f);
9548 b.arg(gate)
9549 .arg(up)
9550 .arg(&mut *act)
9551 .arg(&mut *out_q)
9552 .arg(&mut *out_d)
9553 .arg(&nc);
9554 unsafe {
9555 b.launch(cfg)?;
9556 }
9557 Ok(())
9558 }
9559
9560 #[allow(clippy::too_many_arguments)]
9562 pub fn add_rms_norm3_q8z(
9563 &self,
9564 a: &CudaSlice<f32>,
9565 b_in: &CudaSlice<f32>,
9566 w0: &CudaSlice<f32>,
9567 w1: &CudaSlice<f32>,
9568 w2: &CudaSlice<f32>,
9569 res: &mut CudaSlice<f32>,
9570 out1: &mut CudaSlice<f32>,
9571 ncols: usize,
9572 nrows: usize,
9573 eps: f32,
9574 ) -> Result<
9575 (
9576 (CudaSlice<i8>, CudaSlice<f32>),
9577 (CudaSlice<i8>, CudaSlice<f32>),
9578 ),
9579 Box<dyn std::error::Error>,
9580 > {
9581 let mut q0 = self.alloc_uninit::<i8>(nrows * ncols)?;
9582 let mut d0 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9583 let mut q2 = self.alloc_uninit::<i8>(nrows * ncols)?;
9584 let mut d2 = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
9585 let f = self.func("add_rms_norm3_q8z_f32");
9586 let cfg = LaunchConfig {
9587 grid_dim: (nrows as u32, 1, 1),
9588 block_dim: (rms_block(), 1, 1),
9589 shared_mem_bytes: 0,
9590 };
9591 let (nc, e2) = (ncols as i32, eps);
9592 let __s_b = self.gpu.stream();
9593 let mut b = __s_b.launch_builder(&f);
9594 b.arg(a)
9595 .arg(b_in)
9596 .arg(w0)
9597 .arg(w1)
9598 .arg(w2)
9599 .arg(res)
9600 .arg(&mut q0)
9601 .arg(&mut d0)
9602 .arg(out1)
9603 .arg(&mut q2)
9604 .arg(&mut d2)
9605 .arg(&nc)
9606 .arg(&e2);
9607 unsafe {
9608 b.launch(cfg)?;
9609 }
9610 Ok(((q0, d0), (q2, d2)))
9611 }
9612
9613 #[allow(clippy::too_many_arguments)]
9615 pub fn add_rms_norm3(
9616 &self,
9617 a: &CudaSlice<f32>,
9618 b_in: &CudaSlice<f32>,
9619 w0: &CudaSlice<f32>,
9620 w1: &CudaSlice<f32>,
9621 w2: &CudaSlice<f32>,
9622 res: &mut CudaSlice<f32>,
9623 d0: &mut CudaSlice<f32>,
9624 d1: &mut CudaSlice<f32>,
9625 d2: &mut CudaSlice<f32>,
9626 ncols: usize,
9627 nrows: usize,
9628 eps: f32,
9629 ) -> Result<(), Box<dyn std::error::Error>> {
9630 let f = self.func("add_rms_norm3_f32");
9631 let cfg = LaunchConfig {
9632 grid_dim: (nrows as u32, 1, 1),
9633 block_dim: (rms_block(), 1, 1),
9634 shared_mem_bytes: 0,
9635 };
9636 let (nc, e2) = (ncols as i32, eps);
9637 let __s_b = self.gpu.stream();
9638 let mut b = __s_b.launch_builder(&f);
9639 b.arg(a)
9640 .arg(b_in)
9641 .arg(w0)
9642 .arg(w1)
9643 .arg(w2)
9644 .arg(res)
9645 .arg(d0)
9646 .arg(d1)
9647 .arg(d2)
9648 .arg(&nc)
9649 .arg(&e2);
9650 unsafe {
9651 b.launch(cfg)?;
9652 }
9653 Ok(())
9654 }
9655
9656 pub fn add_scale(
9658 &self,
9659 a: &CudaSlice<f32>,
9660 b_in: &CudaSlice<f32>,
9661 c: f32,
9662 dst: &mut CudaSlice<f32>,
9663 n: usize,
9664 ) -> Result<(), Box<dyn std::error::Error>> {
9665 let f = self.func("add_scale_f32");
9666 let cfg = LaunchConfig::for_num_elems(n as u32);
9667 let ni = n as i32;
9668 let __s_b = self.gpu.stream();
9669 let mut b = __s_b.launch_builder(&f);
9670 b.arg(a).arg(b_in).arg(&c).arg(dst).arg(&ni);
9671 unsafe {
9672 b.launch(cfg)?;
9673 }
9674 Ok(())
9675 }
9676
9677 pub fn layer_norm_bias(
9679 &self,
9680 x: &CudaSlice<f32>,
9681 w: &CudaSlice<f32>,
9682 b: &CudaSlice<f32>,
9683 dst: &mut CudaSlice<f32>,
9684 ncols: usize,
9685 nrows: usize,
9686 eps: f32,
9687 ) -> Result<(), Box<dyn std::error::Error>> {
9688 let f = self.func("layer_norm_bias_f32");
9689 let (nc, e) = (ncols as i32, eps);
9690 let cfg = LaunchConfig {
9691 grid_dim: (nrows as u32, 1, 1),
9692 block_dim: (256, 1, 1),
9693 shared_mem_bytes: 0,
9694 };
9695 let __s_b = self.gpu.stream();
9696 let mut lb = __s_b.launch_builder(&f);
9697 lb.arg(x).arg(w).arg(b).arg(&mut *dst).arg(&nc).arg(&e);
9698 unsafe {
9699 lb.launch(cfg)?;
9700 }
9701 Ok(())
9702 }
9703
9704 pub fn gelu_tanh(
9706 &self,
9707 x: &CudaSlice<f32>,
9708 dst: &mut CudaSlice<f32>,
9709 n: usize,
9710 ) -> Result<(), Box<dyn std::error::Error>> {
9711 let f = self.func("gelu_tanh_f32");
9712 let ni = n as i64;
9713 let cfg = LaunchConfig {
9714 grid_dim: (n.div_ceil(256) as u32, 1, 1),
9715 block_dim: (256, 1, 1),
9716 shared_mem_bytes: 0,
9717 };
9718 let __s_b = self.gpu.stream();
9719 let mut lb = __s_b.launch_builder(&f);
9720 lb.arg(x).arg(&mut *dst).arg(&ni);
9721 unsafe {
9722 lb.launch(cfg)?;
9723 }
9724 Ok(())
9725 }
9726
9727 pub fn row_softmax(
9729 &self,
9730 x: &mut CudaSlice<f32>,
9731 ncols: usize,
9732 nrows: usize,
9733 ) -> Result<(), Box<dyn std::error::Error>> {
9734 let f = self.func("row_softmax_f32");
9735 let nc = ncols as i32;
9736 let cfg = LaunchConfig {
9737 grid_dim: (nrows as u32, 1, 1),
9738 block_dim: (256, 1, 1),
9739 shared_mem_bytes: 0,
9740 };
9741 let __s_b = self.gpu.stream();
9742 let mut lb = __s_b.launch_builder(&f);
9743 lb.arg(&mut *x).arg(&nc);
9744 unsafe {
9745 lb.launch(cfg)?;
9746 }
9747 Ok(())
9748 }
9749
9750 pub fn rms_norm(
9751 &self,
9752 x: &CudaSlice<f32>,
9753 w: &CudaSlice<f32>,
9754 dst: &mut CudaSlice<f32>,
9755 ncols: usize,
9756 nrows: usize,
9757 eps: f32,
9758 ) -> Result<(), Box<dyn std::error::Error>> {
9759 let (nc, e) = (ncols as i32, eps);
9760 let kname = if Self::norm_ilp_on() {
9761 "rms_norm_f32_v2"
9762 } else {
9763 "rms_norm_f32"
9764 };
9765 if Self::pdl_on() && Self::pdl_wb_on() {
9766 use cudarc::driver::{DevicePtr, DevicePtrMut};
9767 let s = &self.gpu.stream();
9768 let (px, _g0) = x.device_ptr(s);
9769 let (pw, _g1) = w.device_ptr(s);
9770 let (pd, _g2) = dst.device_ptr_mut(s);
9771 let mut ps = [
9772 &px as *const _ as *mut std::ffi::c_void,
9773 &pw as *const _ as *mut _,
9774 &pd as *const _ as *mut _,
9775 &nc as *const _ as *mut _,
9776 &e as *const _ as *mut _,
9777 ];
9778 unsafe {
9779 self.launch_pdl(kname, (nrows as u32, 1, 1), (rms_block(), 1, 1), &mut ps)?;
9780 }
9781 return Ok(());
9782 }
9783 let f = self.func(kname);
9784 let cfg = LaunchConfig {
9785 grid_dim: (nrows as u32, 1, 1),
9786 block_dim: (rms_block(), 1, 1),
9787 shared_mem_bytes: 0,
9788 };
9789 let __s_b = self.gpu.stream();
9790 let mut b = __s_b.launch_builder(&f);
9791 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
9792 unsafe {
9793 b.launch(cfg)?;
9794 }
9795 Ok(())
9796 }
9797
9798 pub fn rms_norm_decode(
9806 &self,
9807 x: &CudaSlice<f32>,
9808 w: &CudaSlice<f32>,
9809 dst: &mut CudaSlice<f32>,
9810 ncols: usize,
9811 nrows: usize,
9812 eps: f32,
9813 ) -> Result<(), Box<dyn std::error::Error>> {
9814 let f = self.func(if Self::norm_ilp_on() {
9815 "rms_norm_f32_v2"
9816 } else {
9817 "rms_norm_f32"
9818 });
9819 let cfg = LaunchConfig {
9820 grid_dim: (nrows as u32, 1, 1),
9821 block_dim: (1024, 1, 1),
9822 shared_mem_bytes: 0,
9823 };
9824 let (nc, e) = (ncols as i32, eps);
9825 let __s_b = self.gpu.stream();
9826 let mut b = __s_b.launch_builder(&f);
9827 b.arg(x).arg(w).arg(dst).arg(&nc).arg(&e);
9828 unsafe {
9829 b.launch(cfg)?;
9830 }
9831 Ok(())
9832 }
9833
9834 pub fn rms_norm_q8_1(
9838 &self,
9839 x: &CudaSlice<f32>,
9840 w: &CudaSlice<f32>,
9841 ncols: usize,
9842 nrows: usize,
9843 eps: f32,
9844 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9845 let nblk = ncols / 32;
9846 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
9847 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
9848 let (nc, e) = (ncols as i32, eps);
9849 if Self::pdl_on() {
9850 {
9851 use cudarc::driver::{DevicePtr, DevicePtrMut};
9852 let s = &self.gpu.stream();
9853 let (px, _g0) = x.device_ptr(s);
9854 let (pw, _g1) = w.device_ptr(s);
9855 let (pq, _g2) = q.device_ptr_mut(s);
9856 let (pd, _g3) = d.device_ptr_mut(s);
9857 let mut ps = [
9858 &px as *const _ as *mut std::ffi::c_void,
9859 &pw as *const _ as *mut _,
9860 &pq as *const _ as *mut _,
9861 &pd as *const _ as *mut _,
9862 &nc as *const _ as *mut _,
9863 &e as *const _ as *mut _,
9864 ];
9865 unsafe {
9866 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
9867 }
9868 }
9869 return Ok((q, d));
9870 }
9871 let f = self.func("rms_norm_q8_1");
9872 let cfg = LaunchConfig {
9875 grid_dim: (nrows as u32, 1, 1),
9876 block_dim: (1024, 1, 1),
9877 shared_mem_bytes: 0,
9878 };
9879 let __s_b = self.gpu.stream();
9880 let mut b = __s_b.launch_builder(&f);
9881 b.arg(x).arg(w).arg(&mut q).arg(&mut d).arg(&nc).arg(&e);
9882 unsafe {
9883 b.launch(cfg)?;
9884 }
9885 Ok((q, d))
9886 }
9887
9888 pub fn rms_norm_q8_1_into(
9891 &self,
9892 x: &CudaSlice<f32>,
9893 w: &CudaSlice<f32>,
9894 ncols: usize,
9895 nrows: usize,
9896 eps: f32,
9897 q: &mut CudaSlice<i8>,
9898 d: &mut CudaSlice<f32>,
9899 ) -> Result<(), Box<dyn std::error::Error>> {
9900 let nblk = ncols / 32;
9901 debug_assert!(q.len() >= nrows * ncols && d.len() >= nrows * nblk);
9902 let (nc, e) = (ncols as i32, eps);
9903 if Self::pdl_on() {
9904 use cudarc::driver::{DevicePtr, DevicePtrMut};
9905 let s = &self.gpu.stream();
9906 let (px, _g0) = x.device_ptr(s);
9907 let (pw, _g1) = w.device_ptr(s);
9908 let (pq, _g2) = q.device_ptr_mut(s);
9909 let (pd, _g3) = d.device_ptr_mut(s);
9910 let mut ps = [
9911 &px as *const _ as *mut std::ffi::c_void,
9912 &pw as *const _ as *mut _,
9913 &pq as *const _ as *mut _,
9914 &pd as *const _ as *mut _,
9915 &nc as *const _ as *mut _,
9916 &e as *const _ as *mut _,
9917 ];
9918 unsafe {
9919 self.launch_pdl("rms_norm_q8_1", (nrows as u32, 1, 1), (1024, 1, 1), &mut ps)?;
9920 }
9921 return Ok(());
9922 }
9923 let f = self.func("rms_norm_q8_1");
9924 let cfg = LaunchConfig {
9925 grid_dim: (nrows as u32, 1, 1),
9926 block_dim: (1024, 1, 1),
9927 shared_mem_bytes: 0,
9928 };
9929 let __s_b = self.gpu.stream();
9930 let mut b = __s_b.launch_builder(&f);
9931 b.arg(x).arg(w).arg(&mut *q).arg(&mut *d).arg(&nc).arg(&e);
9932 unsafe {
9933 b.launch(cfg)?;
9934 }
9935 Ok(())
9936 }
9937
9938 pub fn quantize_q8_1_into(
9940 &self,
9941 x: &CudaSlice<f32>,
9942 m: usize,
9943 in_f: usize,
9944 q: &mut CudaSlice<i8>,
9945 d: &mut CudaSlice<f32>,
9946 ) -> Result<(), Box<dyn std::error::Error>> {
9947 let nblk = in_f / 32;
9948 debug_assert!(q.len() >= m * in_f && d.len() >= m * nblk);
9949 let cfg = LaunchConfig::for_num_elems((m * in_f) as u32);
9950 let (inf, mi) = (in_f as i32, m as i32);
9951 if Self::pdl_on() && Self::pdl_wb_on() {
9952 use cudarc::driver::{DevicePtr, DevicePtrMut};
9953 let s = &self.gpu.stream();
9954 let (px, _g0) = x.device_ptr(s);
9955 let (pq, _g1) = q.device_ptr_mut(s);
9956 let (pd, _g2) = d.device_ptr_mut(s);
9957 let mut ps = [
9958 &px as *const _ as *mut std::ffi::c_void,
9959 &pq as *const _ as *mut _,
9960 &pd as *const _ as *mut _,
9961 &inf as *const _ as *mut _,
9962 &mi as *const _ as *mut _,
9963 ];
9964 unsafe {
9965 self.launch_pdl("quantize_q8_1", cfg.grid_dim, cfg.block_dim, &mut ps)?;
9966 }
9967 return Ok(());
9968 }
9969 let f = self.func("quantize_q8_1");
9970 let __s_b = self.gpu.stream();
9971 let mut b = __s_b.launch_builder(&f);
9972 b.arg(x).arg(&mut *q).arg(&mut *d).arg(&inf).arg(&mi);
9973 unsafe {
9974 b.launch(cfg)?;
9975 }
9976 Ok(())
9977 }
9978
9979 pub fn add_rms_norm_q8_1(
9983 &self,
9984 a: &CudaSlice<f32>,
9985 b_in: &CudaSlice<f32>,
9986 w: &CudaSlice<f32>,
9987 res: &mut CudaSlice<f32>,
9988 ncols: usize,
9989 nrows: usize,
9990 eps: f32,
9991 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
9992 let nblk = ncols / 32;
9993 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
9994 let mut d = self.alloc_uninit::<f32>(nrows * nblk)?;
9995 let f = self.func("add_rms_norm_q8_1");
9996 let cfg = LaunchConfig {
9998 grid_dim: (nrows as u32, 1, 1),
9999 block_dim: (1024, 1, 1),
10000 shared_mem_bytes: 0,
10001 };
10002 let (nc, e) = (ncols as i32, eps);
10003 let __s_bld = self.gpu.stream();
10004 let mut bld = __s_bld.launch_builder(&f);
10005 bld.arg(a)
10006 .arg(b_in)
10007 .arg(w)
10008 .arg(res)
10009 .arg(&mut q)
10010 .arg(&mut d)
10011 .arg(&nc)
10012 .arg(&e);
10013 unsafe {
10014 bld.launch(cfg)?;
10015 }
10016 Ok((q, d))
10017 }
10018
10019 #[allow(clippy::too_many_arguments)]
10025 pub fn join_add_rms_norm_raw(
10026 &self,
10027 a0_raw: u64,
10028 a1_raw: u64,
10029 x: &CudaSlice<f32>,
10030 w: &CudaSlice<f32>,
10031 res: &mut CudaSlice<f32>,
10032 dst: &mut CudaSlice<f32>,
10033 ncols: usize,
10034 eps: f32,
10035 ) -> Result<(), Box<dyn std::error::Error>> {
10036 if a0_raw == 0 || a1_raw == 0 || x.len() < ncols || res.len() < ncols || dst.len() < ncols {
10037 return Err("join_add_rms_norm geometry".into());
10038 }
10039 let f = self.func("join_add_rms_norm_f32");
10040 let cfg = LaunchConfig {
10041 grid_dim: (1, 1, 1),
10042 block_dim: (rms_block(), 1, 1),
10043 shared_mem_bytes: 0,
10044 };
10045 let (nc, e) = (ncols as i32, eps);
10046 let __s_b = self.gpu.stream();
10047 let mut b = __s_b.launch_builder(&f);
10048 b.arg(&a0_raw)
10049 .arg(&a1_raw)
10050 .arg(x)
10051 .arg(w)
10052 .arg(&mut *res)
10053 .arg(&mut *dst)
10054 .arg(&nc)
10055 .arg(&e);
10056 unsafe {
10057 b.launch(cfg)?;
10058 }
10059 Ok(())
10060 }
10061
10062 pub fn add_rms_norm(
10063 &self,
10064 a: &CudaSlice<f32>,
10065 b: &CudaSlice<f32>,
10066 w: &CudaSlice<f32>,
10067 res: &mut CudaSlice<f32>,
10068 dst: &mut CudaSlice<f32>,
10069 ncols: usize,
10070 nrows: usize,
10071 eps: f32,
10072 ) -> Result<(), Box<dyn std::error::Error>> {
10073 let (nc, e) = (ncols as i32, eps);
10074 let kname = if Self::norm_ilp_on() {
10075 "add_rms_norm_f32_v2"
10076 } else {
10077 "add_rms_norm_f32"
10078 };
10079 if Self::pdl_on() && Self::pdl_wb_on() {
10080 use cudarc::driver::{DevicePtr, DevicePtrMut};
10081 let s = &self.gpu.stream();
10082 let (pa, _g0) = a.device_ptr(s);
10083 let (pb, _g1) = b.device_ptr(s);
10084 let (pw, _g2) = w.device_ptr(s);
10085 let (pr, _g3) = res.device_ptr_mut(s);
10086 let (pd, _g4) = dst.device_ptr_mut(s);
10087 let mut ps = [
10088 &pa as *const _ as *mut std::ffi::c_void,
10089 &pb as *const _ as *mut _,
10090 &pw as *const _ as *mut _,
10091 &pr as *const _ as *mut _,
10092 &pd as *const _ as *mut _,
10093 &nc as *const _ as *mut _,
10094 &e as *const _ as *mut _,
10095 ];
10096 unsafe {
10097 self.launch_pdl(kname, (nrows as u32, 1, 1), (rms_block(), 1, 1), &mut ps)?;
10098 }
10099 return Ok(());
10100 }
10101 let f = self.func(kname);
10102 let cfg = LaunchConfig {
10103 grid_dim: (nrows as u32, 1, 1),
10104 block_dim: (rms_block(), 1, 1),
10105 shared_mem_bytes: 0,
10106 };
10107 let __s_b2 = self.gpu.stream();
10108 let mut b2 = __s_b2.launch_builder(&f);
10109 b2.arg(a)
10110 .arg(b)
10111 .arg(w)
10112 .arg(&mut *res)
10113 .arg(&mut *dst)
10114 .arg(&nc)
10115 .arg(&e);
10116 unsafe {
10117 b2.launch(cfg)?;
10118 }
10119 Ok(())
10120 }
10121
10122 #[allow(clippy::too_many_arguments)]
10125 pub fn rms_pre_add_rms_norm(
10126 &self,
10127 a: &CudaSlice<f32>,
10128 wa: &CudaSlice<f32>,
10129 b: &CudaSlice<f32>,
10130 w: &CudaSlice<f32>,
10131 res: &mut CudaSlice<f32>,
10132 dst: &mut CudaSlice<f32>,
10133 ncols: usize,
10134 nrows: usize,
10135 eps: f32,
10136 ) -> Result<(), Box<dyn std::error::Error>> {
10137 let f = self.func("rms_pre_add_rms_norm_f32");
10138 let cfg = LaunchConfig {
10139 grid_dim: (nrows as u32, 1, 1),
10140 block_dim: (rms_block(), 1, 1),
10141 shared_mem_bytes: 0,
10142 };
10143 let (nc, e) = (ncols as i32, eps);
10144 let __s_b2 = self.gpu.stream();
10145 let mut b2 = __s_b2.launch_builder(&f);
10146 b2.arg(a)
10147 .arg(wa)
10148 .arg(b)
10149 .arg(w)
10150 .arg(&mut *res)
10151 .arg(&mut *dst)
10152 .arg(&nc)
10153 .arg(&e);
10154 unsafe {
10155 b2.launch(cfg)?;
10156 }
10157 Ok(())
10158 }
10159
10160 #[allow(clippy::too_many_arguments)]
10162 pub fn rms_pre_add_rms_norm_q8z(
10163 &self,
10164 a: &CudaSlice<f32>,
10165 wa: &CudaSlice<f32>,
10166 b: &CudaSlice<f32>,
10167 w: &CudaSlice<f32>,
10168 res: &mut CudaSlice<f32>,
10169 dst: &mut CudaSlice<f32>,
10170 ncols: usize,
10171 nrows: usize,
10172 eps: f32,
10173 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
10174 debug_assert!(ncols % 128 == 0);
10175 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
10176 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
10177 let (nc, e) = (ncols as i32, eps);
10178 if Self::pdl_on() {
10179 {
10180 use cudarc::driver::{DevicePtr, DevicePtrMut};
10181 let s = &self.gpu.stream();
10182 let (pa, _g0) = a.device_ptr(s);
10183 let (pwa, _g1) = wa.device_ptr(s);
10184 let (pb, _g2) = b.device_ptr(s);
10185 let (pw, _g3) = w.device_ptr(s);
10186 let (pr, _g4) = res.device_ptr_mut(s);
10187 let (pdst, _g5) = dst.device_ptr_mut(s);
10188 let (pq, _g6) = out_q.device_ptr_mut(s);
10189 let (pd, _g7) = out_d.device_ptr_mut(s);
10190 let mut ps = [
10191 &pa as *const _ as *mut std::ffi::c_void,
10192 &pwa as *const _ as *mut _,
10193 &pb as *const _ as *mut _,
10194 &pw as *const _ as *mut _,
10195 &pr as *const _ as *mut _,
10196 &pdst as *const _ as *mut _,
10197 &pq as *const _ as *mut _,
10198 &pd as *const _ as *mut _,
10199 &nc as *const _ as *mut _,
10200 &e as *const _ as *mut _,
10201 ];
10202 unsafe {
10203 self.launch_pdl(
10204 "rms_pre_add_rms_norm_q8z_f32",
10205 (nrows as u32, 1, 1),
10206 (rms_block(), 1, 1),
10207 &mut ps,
10208 )?;
10209 }
10210 }
10211 return Ok((out_q, out_d));
10212 }
10213 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
10214 let cfg = LaunchConfig {
10215 grid_dim: (nrows as u32, 1, 1),
10216 block_dim: (rms_block(), 1, 1),
10217 shared_mem_bytes: 0,
10218 };
10219 let __s_b2 = self.gpu.stream();
10220 let mut b2 = __s_b2.launch_builder(&f);
10221 b2.arg(a)
10222 .arg(wa)
10223 .arg(b)
10224 .arg(w)
10225 .arg(&mut *res)
10226 .arg(&mut *dst)
10227 .arg(&mut out_q)
10228 .arg(&mut out_d)
10229 .arg(&nc)
10230 .arg(&e);
10231 unsafe {
10232 b2.launch(cfg)?;
10233 }
10234 Ok((out_q, out_d))
10235 }
10236
10237 #[allow(clippy::too_many_arguments)]
10241 pub fn rms_pre_add_rms_norm_q8z_into(
10242 &self,
10243 a: &CudaSlice<f32>,
10244 wa: &CudaSlice<f32>,
10245 b: &CudaSlice<f32>,
10246 w: &CudaSlice<f32>,
10247 res: &mut CudaSlice<f32>,
10248 dst: &mut CudaSlice<f32>,
10249 ncols: usize,
10250 nrows: usize,
10251 eps: f32,
10252 out_q: &mut CudaSlice<i8>,
10253 out_d: &mut CudaSlice<f32>,
10254 ) -> Result<(), Box<dyn std::error::Error>> {
10255 debug_assert!(ncols % 128 == 0);
10256 let (nc, e) = (ncols as i32, eps);
10257 let f = self.func("rms_pre_add_rms_norm_q8z_f32");
10258 let cfg = LaunchConfig {
10259 grid_dim: (nrows as u32, 1, 1),
10260 block_dim: (rms_block(), 1, 1),
10261 shared_mem_bytes: 0,
10262 };
10263 let __s_b = self.gpu.stream();
10264 let mut b2 = __s_b.launch_builder(&f);
10265 b2.arg(a)
10266 .arg(wa)
10267 .arg(b)
10268 .arg(w)
10269 .arg(&mut *res)
10270 .arg(&mut *dst)
10271 .arg(&mut *out_q)
10272 .arg(&mut *out_d)
10273 .arg(&nc)
10274 .arg(&e);
10275 unsafe {
10276 b2.launch(cfg)?;
10277 }
10278 Ok(())
10279 }
10280
10281 #[allow(clippy::too_many_arguments)]
10284 pub fn rms_pre_add_scale_rms_norm_q8_1_into(
10285 &self,
10286 a: &CudaSlice<f32>,
10287 wa: &CudaSlice<f32>,
10288 b_in: &CudaSlice<f32>,
10289 c: f32,
10290 w: &CudaSlice<f32>,
10291 res: &mut CudaSlice<f32>,
10292 ncols: usize,
10293 nrows: usize,
10294 eps: f32,
10295 out_q: &mut CudaSlice<i8>,
10296 out_d: &mut CudaSlice<f32>,
10297 ) -> Result<(), Box<dyn std::error::Error>> {
10298 debug_assert!(ncols % 128 == 0);
10299 let (nc, e2) = (ncols as i32, eps);
10300 let f = self.func("rms_pre_add_scale_rms_norm_q8_1");
10301 let cfg = LaunchConfig {
10302 grid_dim: (nrows as u32, 1, 1),
10303 block_dim: (rms_block(), 1, 1),
10304 shared_mem_bytes: 0,
10305 };
10306 let __s_b = self.gpu.stream();
10307 let mut b2 = __s_b.launch_builder(&f);
10308 b2.arg(a)
10309 .arg(wa)
10310 .arg(b_in)
10311 .arg(&c)
10312 .arg(w)
10313 .arg(&mut *res)
10314 .arg(&mut *out_q)
10315 .arg(&mut *out_d)
10316 .arg(&nc)
10317 .arg(&e2);
10318 unsafe {
10319 b2.launch(cfg)?;
10320 }
10321 Ok(())
10322 }
10323
10324 pub fn g4_pnfold_on() -> bool {
10332 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
10333 *ON.get_or_init(|| {
10334 std::env::var("MEMRA_G4_PNFOLD")
10335 .map(|v| v != "0")
10336 .unwrap_or(true)
10337 })
10338 }
10339
10340 pub fn build_q4_out_concat3(
10344 &self,
10345 w0: &crate::model::GpuTensor,
10346 w1: &crate::model::GpuTensor,
10347 w2: &crate::model::GpuTensor,
10348 ) -> Result<Option<crate::model::GpuTensor>, Box<dyn std::error::Error>> {
10349 use crate::model::GpuTensor;
10350 let part = |w: &GpuTensor| -> Option<(usize, usize)> {
10351 match w {
10352 GpuTensor::Quant {
10353 qtype,
10354 row_bytes,
10355 rp,
10356 ..
10357 } if *qtype == QT_Q4_0 && !*rp => Some((*row_bytes, w.out_features())),
10358 _ => None,
10359 }
10360 };
10361 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (part(w0), part(w1), part(w2))
10362 else {
10363 return Ok(None);
10364 };
10365 if rb0 != rb1
10366 || rb0 != rb2
10367 || w0.in_features() != w1.in_features()
10368 || w0.in_features() != w2.in_features()
10369 {
10370 return Ok(None);
10371 }
10372 fn bytes_of(w: &crate::model::GpuTensor) -> &CudaSlice<u8> {
10373 match w {
10374 crate::model::GpuTensor::Quant { bytes, .. } => bytes,
10375 _ => unreachable!(),
10376 }
10377 }
10378 let (b0, b1, b2) = (bytes_of(w0), bytes_of(w1), bytes_of(w2));
10379 let total = rb0 * (o0 + o1 + o2);
10380 let mut cat = self.alloc_u8(total)?;
10381 self.copy_u8_into(&mut cat, 0, b0, rb0 * o0)?;
10382 self.copy_u8_into(&mut cat, rb0 * o0, b1, rb1 * o1)?;
10383 self.copy_u8_into(&mut cat, rb0 * (o0 + o1), b2, rb2 * o2)?;
10384 Ok(Some(GpuTensor::Quant {
10385 bytes: cat,
10386 qtype: QT_Q4_0,
10387 row_bytes: rb0,
10388 ne: vec![w0.in_features() as u64, (o0 + o1 + o2) as u64],
10389 scale: 1.0,
10390 rp: false,
10391 #[cfg(memra_cutlass)]
10392 cutlass: None,
10393 fp8: None,
10394 blk: None,
10395 rp4: None,
10396 f16: None,
10397 }))
10398 }
10399
10400 fn full_width_rope_only(
10418 kernel: &str,
10419 n_rot: usize,
10420 head_dim: usize,
10421 ) -> Result<(), Box<dyn std::error::Error>> {
10422 if n_rot == head_dim {
10423 return Ok(());
10424 }
10425 Err(format!(
10426 "{kernel}: PARTIAL ROTARY REFUSED — n_rot {n_rot} != head_dim {head_dim}. This fused \
10427 rms_norm+qkv+rope kernel carries no n_dims parameter and rotates the full head \
10428 width (half = ncols/2), so it would rotate dims {n_rot}..{head_dim} that must pass \
10429 through unrotated. Use the split path (rms_norm_qkv + rope_neox/rope_neox2 with \
10430 n_dims={n_rot}), or add an n_dims early-return to the kernel and widen this guard."
10431 )
10432 .into())
10433 }
10434
10435 #[allow(clippy::too_many_arguments)]
10439 pub fn rms_norm_qkv_rope_cat(
10440 &self,
10441 qkv: &CudaSlice<f32>,
10442 wq: &CudaSlice<f32>,
10443 wk: &CudaSlice<f32>,
10444 wv: &CudaSlice<f32>,
10445 q: &mut CudaSlice<f32>,
10446 k: &mut CudaSlice<f32>,
10447 v: &mut CudaSlice<f32>,
10448 head_dim: usize,
10449 n_rot: usize,
10450 rq: usize,
10451 rk: usize,
10452 pos: &CudaSlice<i32>,
10453 nh_q: usize,
10454 nh_k: usize,
10455 base: f32,
10456 freq_scale: f32,
10457 ff: Option<&CudaSlice<f32>>,
10458 eps: f32,
10459 ) -> Result<(), Box<dyn std::error::Error>> {
10460 Self::full_width_rope_only("rms_norm_qkv_rope_cat", n_rot, head_dim)?;
10461 let rows = rq + rk + rk;
10462 let theta_scale = base.powf(-2.0 / head_dim as f32);
10463 let (nc, rqi, rki, nhq, nhk) = (
10464 head_dim as i32,
10465 rq as i32,
10466 rk as i32,
10467 nh_q as i32,
10468 nh_k as i32,
10469 );
10470 if Self::pdl_on() {
10471 use cudarc::driver::{DevicePtr, DevicePtrMut};
10472 let s = &self.gpu.stream();
10473 let (pqkv, _g0) = qkv.device_ptr(s);
10474 let (pwq, _g1) = wq.device_ptr(s);
10475 let (pwk, _g2) = wk.device_ptr(s);
10476 let (pwv, _g3) = wv.device_ptr(s);
10477 let (pq, _g4) = q.device_ptr_mut(s);
10478 let (pk, _g5) = k.device_ptr_mut(s);
10479 let (pv, _g6) = v.device_ptr_mut(s);
10480 let (ppos, _g7) = pos.device_ptr(s);
10481 let (pff, _g8) = match ff {
10482 Some(t) => {
10483 let (p, g) = t.device_ptr(s);
10484 (p, Some(g))
10485 }
10486 None => (0, None),
10487 };
10488 let mut ps = [
10489 &pqkv as *const _ as *mut std::ffi::c_void,
10490 &pwq as *const _ as *mut _,
10491 &pwk as *const _ as *mut _,
10492 &pwv as *const _ as *mut _,
10493 &pq as *const _ as *mut _,
10494 &pk as *const _ as *mut _,
10495 &pv as *const _ as *mut _,
10496 &nc as *const _ as *mut _,
10497 &rqi as *const _ as *mut _,
10498 &rki as *const _ as *mut _,
10499 &ppos as *const _ as *mut _,
10500 &nhq as *const _ as *mut _,
10501 &nhk as *const _ as *mut _,
10502 &theta_scale as *const _ as *mut _,
10503 &freq_scale as *const _ as *mut _,
10504 &pff as *const _ as *mut _,
10505 &eps as *const _ as *mut _,
10506 ];
10507 unsafe {
10508 self.launch_pdl(
10509 "rms_norm_qkv_rope_cat_f32",
10510 (rows as u32, 1, 1),
10511 (rms_block(), 1, 1),
10512 &mut ps,
10513 )?;
10514 }
10515 return Ok(());
10516 }
10517 let f = self.func("rms_norm_qkv_rope_cat_f32");
10518 let cfg = LaunchConfig {
10519 grid_dim: (rows as u32, 1, 1),
10520 block_dim: (rms_block(), 1, 1),
10521 shared_mem_bytes: 0,
10522 };
10523 let __s_b = self.gpu.stream();
10524 let mut b = __s_b.launch_builder(&f);
10525 match ff {
10526 Some(t) => {
10527 b.arg(qkv)
10528 .arg(wq)
10529 .arg(wk)
10530 .arg(wv)
10531 .arg(&mut *q)
10532 .arg(&mut *k)
10533 .arg(&mut *v)
10534 .arg(&nc)
10535 .arg(&rqi)
10536 .arg(&rki)
10537 .arg(pos)
10538 .arg(&nhq)
10539 .arg(&nhk)
10540 .arg(&theta_scale)
10541 .arg(&freq_scale)
10542 .arg(t)
10543 .arg(&eps);
10544 unsafe {
10545 b.launch(cfg)?;
10546 }
10547 }
10548 None => {
10549 let null: u64 = 0;
10550 b.arg(qkv)
10551 .arg(wq)
10552 .arg(wk)
10553 .arg(wv)
10554 .arg(&mut *q)
10555 .arg(&mut *k)
10556 .arg(&mut *v)
10557 .arg(&nc)
10558 .arg(&rqi)
10559 .arg(&rki)
10560 .arg(pos)
10561 .arg(&nhq)
10562 .arg(&nhk)
10563 .arg(&theta_scale)
10564 .arg(&freq_scale)
10565 .arg(&null)
10566 .arg(&eps);
10567 unsafe {
10568 b.launch(cfg)?;
10569 }
10570 }
10571 }
10572 Ok(())
10573 }
10574
10575 #[allow(clippy::too_many_arguments)]
10579 pub fn rms_norm_qkv_rope(
10580 &self,
10581 q0: &CudaSlice<f32>,
10582 k0: &CudaSlice<f32>,
10583 v0: &CudaSlice<f32>,
10584 wq: &CudaSlice<f32>,
10585 wk: &CudaSlice<f32>,
10586 wv: &CudaSlice<f32>,
10587 q: &mut CudaSlice<f32>,
10588 k: &mut CudaSlice<f32>,
10589 v: &mut CudaSlice<f32>,
10590 head_dim: usize,
10591 n_rot: usize,
10592 rq: usize,
10593 rk: usize,
10594 pos: &CudaSlice<i32>,
10595 nh_q: usize,
10596 nh_k: usize,
10597 base: f32,
10598 freq_scale: f32,
10599 ff: Option<&CudaSlice<f32>>,
10600 eps: f32,
10601 ) -> Result<(), Box<dyn std::error::Error>> {
10602 Self::full_width_rope_only("rms_norm_qkv_rope", n_rot, head_dim)?;
10603 let f = self.func("rms_norm_qkv_rope_f32");
10604 let rows = rq + rk + rk; let cfg = LaunchConfig {
10606 grid_dim: (rows as u32, 1, 1),
10607 block_dim: (rms_block(), 1, 1),
10608 shared_mem_bytes: 0,
10609 };
10610 let theta_scale = base.powf(-2.0 / head_dim as f32);
10611 let (nc, rqi, rki, nhq, nhk) = (
10612 head_dim as i32,
10613 rq as i32,
10614 rk as i32,
10615 nh_q as i32,
10616 nh_k as i32,
10617 );
10618 let __s_b = self.gpu.stream();
10619 let mut b = __s_b.launch_builder(&f);
10620 match ff {
10621 Some(t) => {
10622 b.arg(q0)
10623 .arg(k0)
10624 .arg(v0)
10625 .arg(wq)
10626 .arg(wk)
10627 .arg(wv)
10628 .arg(&mut *q)
10629 .arg(&mut *k)
10630 .arg(&mut *v)
10631 .arg(&nc)
10632 .arg(&rqi)
10633 .arg(&rki)
10634 .arg(pos)
10635 .arg(&nhq)
10636 .arg(&nhk)
10637 .arg(&theta_scale)
10638 .arg(&freq_scale)
10639 .arg(t)
10640 .arg(&eps);
10641 unsafe {
10642 b.launch(cfg)?;
10643 }
10644 }
10645 None => {
10646 let null: u64 = 0;
10647 b.arg(q0)
10648 .arg(k0)
10649 .arg(v0)
10650 .arg(wq)
10651 .arg(wk)
10652 .arg(wv)
10653 .arg(&mut *q)
10654 .arg(&mut *k)
10655 .arg(&mut *v)
10656 .arg(&nc)
10657 .arg(&rqi)
10658 .arg(&rki)
10659 .arg(pos)
10660 .arg(&nhq)
10661 .arg(&nhk)
10662 .arg(&theta_scale)
10663 .arg(&freq_scale)
10664 .arg(&null)
10665 .arg(&eps);
10666 unsafe {
10667 b.launch(cfg)?;
10668 }
10669 }
10670 }
10671 Ok(())
10672 }
10673
10674 #[allow(clippy::too_many_arguments)]
10680 pub fn rms_norm_qkv_rope_append_dc(
10681 &self,
10682 q0: &CudaSlice<f32>,
10683 k0: &CudaSlice<f32>,
10684 v0: &CudaSlice<f32>,
10685 wq: &CudaSlice<f32>,
10686 wk: &CudaSlice<f32>,
10687 wv: &CudaSlice<f32>,
10688 q: &mut CudaSlice<f32>,
10689 k: &mut CudaSlice<f32>,
10690 v: &mut CudaSlice<f32>,
10691 head_dim: usize,
10692 n_rot: usize,
10693 rq: usize,
10694 rk: usize,
10695 pos: &CudaSlice<i32>,
10696 nh_q: usize,
10697 nh_k: usize,
10698 base: f32,
10699 freq_scale: f32,
10700 ff: Option<&CudaSlice<f32>>,
10701 eps: f32,
10702 kc: &mut CudaSlice<u8>,
10703 vc: &mut CudaSlice<u8>,
10704 t_dev: &CudaSlice<i32>,
10705 k_tok_bytes: usize,
10706 v_tok_bytes: usize,
10707 g: bool,
10708 ) -> Result<(), Box<dyn std::error::Error>> {
10709 Self::full_width_rope_only("rms_norm_qkv_rope_append_dc", n_rot, head_dim)?;
10710 let rows = rq + rk + rk;
10711 let theta_scale = base.powf(-2.0 / head_dim as f32);
10712 let (nc, rqi, rki, nhq, nhk) = (
10713 head_dim as i32,
10714 rq as i32,
10715 rk as i32,
10716 nh_q as i32,
10717 nh_k as i32,
10718 );
10719 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
10720 if Self::pdl_on() && Self::pdl_wb_on() {
10721 use cudarc::driver::{DevicePtr, DevicePtrMut};
10722 let s = &self.gpu.stream();
10723 let (p0, _a0) = q0.device_ptr(s);
10724 let (p1, _a1) = k0.device_ptr(s);
10725 let (p2, _a2) = v0.device_ptr(s);
10726 let (pwq, _a3) = wq.device_ptr(s);
10727 let (pwk, _a4) = wk.device_ptr(s);
10728 let (pwv, _a5) = wv.device_ptr(s);
10729 let (pq, _a6) = q.device_ptr_mut(s);
10730 let (pk, _a7) = k.device_ptr_mut(s);
10731 let (pv, _a8) = v.device_ptr_mut(s);
10732 let (pp, _a9) = pos.device_ptr(s);
10733 let pff: u64 = match ff {
10734 Some(t) => {
10735 let (p, _gg) = t.device_ptr(s);
10736 p as u64
10737 }
10738 None => 0,
10739 };
10740 let (pkc, _a10) = kc.device_ptr_mut(s);
10741 let (pvc, _a11) = vc.device_ptr_mut(s);
10742 let (pt, _a12) = t_dev.device_ptr(s);
10743 let mut ps = [
10744 &p0 as *const _ as *mut std::ffi::c_void,
10745 &p1 as *const _ as *mut _,
10746 &p2 as *const _ as *mut _,
10747 &pwq as *const _ as *mut _,
10748 &pwk as *const _ as *mut _,
10749 &pwv as *const _ as *mut _,
10750 &pq as *const _ as *mut _,
10751 &pk as *const _ as *mut _,
10752 &pv as *const _ as *mut _,
10753 &nc as *const _ as *mut _,
10754 &rqi as *const _ as *mut _,
10755 &rki as *const _ as *mut _,
10756 &pp as *const _ as *mut _,
10757 &nhq as *const _ as *mut _,
10758 &nhk as *const _ as *mut _,
10759 &theta_scale as *const _ as *mut _,
10760 &freq_scale as *const _ as *mut _,
10761 &pff as *const _ as *mut _,
10762 &eps as *const _ as *mut _,
10763 &pkc as *const _ as *mut _,
10764 &pvc as *const _ as *mut _,
10765 &pt as *const _ as *mut _,
10766 &ktb as *const _ as *mut _,
10767 &vtb as *const _ as *mut _,
10768 ];
10769 unsafe {
10770 self.launch_pdl_flash(
10771 g,
10772 "rms_norm_qkv_rope_append_dc_f32",
10773 (rows as u32, 1, 1),
10774 (rms_block(), 1, 1),
10775 0,
10776 &mut ps,
10777 )?;
10778 }
10779 return Ok(());
10780 }
10781 let f = if g {
10782 self.func_g("rms_norm_qkv_rope_append_dc_f32")
10783 } else {
10784 self.func("rms_norm_qkv_rope_append_dc_f32")
10785 };
10786 let cfg = LaunchConfig {
10787 grid_dim: (rows as u32, 1, 1),
10788 block_dim: (rms_block(), 1, 1),
10789 shared_mem_bytes: 0,
10790 };
10791 let __s_b = self.gpu.stream();
10792 let mut b = __s_b.launch_builder(&f);
10793 match ff {
10794 Some(t) => {
10795 b.arg(q0)
10796 .arg(k0)
10797 .arg(v0)
10798 .arg(wq)
10799 .arg(wk)
10800 .arg(wv)
10801 .arg(&mut *q)
10802 .arg(&mut *k)
10803 .arg(&mut *v)
10804 .arg(&nc)
10805 .arg(&rqi)
10806 .arg(&rki)
10807 .arg(pos)
10808 .arg(&nhq)
10809 .arg(&nhk)
10810 .arg(&theta_scale)
10811 .arg(&freq_scale)
10812 .arg(t)
10813 .arg(&eps)
10814 .arg(&mut *kc)
10815 .arg(&mut *vc)
10816 .arg(t_dev)
10817 .arg(&ktb)
10818 .arg(&vtb);
10819 unsafe {
10820 b.launch(cfg)?;
10821 }
10822 }
10823 None => {
10824 let null: u64 = 0;
10825 b.arg(q0)
10826 .arg(k0)
10827 .arg(v0)
10828 .arg(wq)
10829 .arg(wk)
10830 .arg(wv)
10831 .arg(&mut *q)
10832 .arg(&mut *k)
10833 .arg(&mut *v)
10834 .arg(&nc)
10835 .arg(&rqi)
10836 .arg(&rki)
10837 .arg(pos)
10838 .arg(&nhq)
10839 .arg(&nhk)
10840 .arg(&theta_scale)
10841 .arg(&freq_scale)
10842 .arg(&null)
10843 .arg(&eps)
10844 .arg(&mut *kc)
10845 .arg(&mut *vc)
10846 .arg(t_dev)
10847 .arg(&ktb)
10848 .arg(&vtb);
10849 unsafe {
10850 b.launch(cfg)?;
10851 }
10852 }
10853 }
10854 Ok(())
10855 }
10856
10857 #[allow(clippy::too_many_arguments)]
10865 pub fn rms_norm_qkv_rope_append(
10866 &self,
10867 q0: &CudaSlice<f32>,
10868 k0: &CudaSlice<f32>,
10869 v0: &CudaSlice<f32>,
10870 wq: &CudaSlice<f32>,
10871 wk: &CudaSlice<f32>,
10872 wv: &CudaSlice<f32>,
10873 q: &mut CudaSlice<f32>,
10874 k: &mut CudaSlice<f32>,
10875 v: &mut CudaSlice<f32>,
10876 head_dim: usize,
10877 n_rot: usize,
10878 rq: usize,
10879 rk: usize,
10880 pos: &CudaSlice<i32>,
10881 nh_q: usize,
10882 nh_k: usize,
10883 base: f32,
10884 freq_scale: f32,
10885 ff: Option<&CudaSlice<f32>>,
10886 eps: f32,
10887 kc: &mut CudaSlice<u8>,
10888 vc: &mut CudaSlice<u8>,
10889 t: usize,
10890 k_tok_bytes: usize,
10891 v_tok_bytes: usize,
10892 g: bool,
10893 ) -> Result<(), Box<dyn std::error::Error>> {
10894 Self::full_width_rope_only("rms_norm_qkv_rope_append", n_rot, head_dim)?;
10895 let rows = rq + rk + rk;
10896 let theta_scale = base.powf(-2.0 / head_dim as f32);
10897 let (nc, rqi, rki, nhq, nhk) = (
10898 head_dim as i32,
10899 rq as i32,
10900 rk as i32,
10901 nh_q as i32,
10902 nh_k as i32,
10903 );
10904 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
10905 let ti = t as i32;
10906 if Self::pdl_on() && Self::pdl_wb_on() {
10907 use cudarc::driver::{DevicePtr, DevicePtrMut};
10908 let s = &self.gpu.stream();
10909 let (p0, _a0) = q0.device_ptr(s);
10910 let (p1, _a1) = k0.device_ptr(s);
10911 let (p2, _a2) = v0.device_ptr(s);
10912 let (pwq, _a3) = wq.device_ptr(s);
10913 let (pwk, _a4) = wk.device_ptr(s);
10914 let (pwv, _a5) = wv.device_ptr(s);
10915 let (pq, _a6) = q.device_ptr_mut(s);
10916 let (pk, _a7) = k.device_ptr_mut(s);
10917 let (pv, _a8) = v.device_ptr_mut(s);
10918 let (pp, _a9) = pos.device_ptr(s);
10919 let pff: u64 = match ff {
10920 Some(t) => {
10921 let (p, _gg) = t.device_ptr(s);
10922 p as u64
10923 }
10924 None => 0,
10925 };
10926 let (pkc, _a10) = kc.device_ptr_mut(s);
10927 let (pvc, _a11) = vc.device_ptr_mut(s);
10928 let mut ps = [
10929 &p0 as *const _ as *mut std::ffi::c_void,
10930 &p1 as *const _ as *mut _,
10931 &p2 as *const _ as *mut _,
10932 &pwq as *const _ as *mut _,
10933 &pwk as *const _ as *mut _,
10934 &pwv as *const _ as *mut _,
10935 &pq as *const _ as *mut _,
10936 &pk as *const _ as *mut _,
10937 &pv as *const _ as *mut _,
10938 &nc as *const _ as *mut _,
10939 &rqi as *const _ as *mut _,
10940 &rki as *const _ as *mut _,
10941 &pp as *const _ as *mut _,
10942 &nhq as *const _ as *mut _,
10943 &nhk as *const _ as *mut _,
10944 &theta_scale as *const _ as *mut _,
10945 &freq_scale as *const _ as *mut _,
10946 &pff as *const _ as *mut _,
10947 &eps as *const _ as *mut _,
10948 &pkc as *const _ as *mut _,
10949 &pvc as *const _ as *mut _,
10950 &ti as *const _ as *mut _,
10951 &ktb as *const _ as *mut _,
10952 &vtb as *const _ as *mut _,
10953 ];
10954 unsafe {
10955 self.launch_pdl_flash(
10956 g,
10957 "rms_norm_qkv_rope_append_f32",
10958 (rows as u32, 1, 1),
10959 (rms_block(), 1, 1),
10960 0,
10961 &mut ps,
10962 )?;
10963 }
10964 return Ok(());
10965 }
10966 let f = if g {
10967 self.func_g("rms_norm_qkv_rope_append_f32")
10968 } else {
10969 self.func("rms_norm_qkv_rope_append_f32")
10970 };
10971 let cfg = LaunchConfig {
10972 grid_dim: (rows as u32, 1, 1),
10973 block_dim: (rms_block(), 1, 1),
10974 shared_mem_bytes: 0,
10975 };
10976 let __s_b = self.gpu.stream();
10977 let mut b = __s_b.launch_builder(&f);
10978 let null: u64 = 0;
10979 b.arg(q0)
10980 .arg(k0)
10981 .arg(v0)
10982 .arg(wq)
10983 .arg(wk)
10984 .arg(wv)
10985 .arg(&mut *q)
10986 .arg(&mut *k)
10987 .arg(&mut *v)
10988 .arg(&nc)
10989 .arg(&rqi)
10990 .arg(&rki)
10991 .arg(pos)
10992 .arg(&nhq)
10993 .arg(&nhk)
10994 .arg(&theta_scale)
10995 .arg(&freq_scale);
10996 match ff {
10997 Some(t) => {
10998 b.arg(t);
10999 }
11000 None => {
11001 b.arg(&null);
11002 }
11003 }
11004 b.arg(&eps)
11005 .arg(&mut *kc)
11006 .arg(&mut *vc)
11007 .arg(&ti)
11008 .arg(&ktb)
11009 .arg(&vtb);
11010 unsafe {
11011 b.launch(cfg)?;
11012 }
11013 Ok(())
11014 }
11015
11016 pub fn add_q8_1(
11017 &self,
11018 a: &CudaSlice<f32>,
11019 b: &CudaSlice<f32>,
11020 res: &mut CudaSlice<f32>,
11021 ncols: usize,
11022 nrows: usize,
11023 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11024 debug_assert!(ncols % 128 == 0);
11025 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
11026 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
11027 let f = self.func("add_q8_1_f32");
11028 let cfg = LaunchConfig {
11029 grid_dim: (nrows as u32, 1, 1),
11030 block_dim: (rms_block(), 1, 1),
11031 shared_mem_bytes: 0,
11032 };
11033 let nc = ncols as i32;
11034 let __s_b2 = self.gpu.stream();
11035 let mut b2 = __s_b2.launch_builder(&f);
11036 b2.arg(a)
11037 .arg(b)
11038 .arg(&mut *res)
11039 .arg(&mut out_q)
11040 .arg(&mut out_d)
11041 .arg(&nc);
11042 unsafe {
11043 b2.launch(cfg)?;
11044 }
11045 Ok((out_q, out_d))
11046 }
11047
11048 pub fn rms_pre_add_q8_1(
11052 &self,
11053 a: &CudaSlice<f32>,
11054 wa: &CudaSlice<f32>,
11055 b: &CudaSlice<f32>,
11056 res: &mut CudaSlice<f32>,
11057 ncols: usize,
11058 nrows: usize,
11059 eps: f32,
11060 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11061 debug_assert!(ncols % 128 == 0);
11062 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
11063 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
11064 let f = self.func("rms_pre_add_q8_1_f32");
11065 let cfg = LaunchConfig {
11066 grid_dim: (nrows as u32, 1, 1),
11067 block_dim: (rms_block(), 1, 1),
11068 shared_mem_bytes: 0,
11069 };
11070 let (nc, ep) = (ncols as i32, eps);
11071 let __s_b2 = self.gpu.stream();
11072 let mut b2 = __s_b2.launch_builder(&f);
11073 b2.arg(a)
11074 .arg(wa)
11075 .arg(b)
11076 .arg(&mut *res)
11077 .arg(&mut out_q)
11078 .arg(&mut out_d)
11079 .arg(&nc)
11080 .arg(&ep);
11081 unsafe {
11082 b2.launch(cfg)?;
11083 }
11084 Ok((out_q, out_d))
11085 }
11086
11087 pub fn l2_v2_on(ncols: usize) -> bool {
11091 ncols == 128 && std::env::var("MEMRA_L2_V2").as_deref() != Ok("0")
11092 }
11093
11094 pub fn l2_norm_pp(
11095 &self,
11096 x: &CudaSlice<f32>,
11097 dst: &mut CudaSlice<f32>,
11098 dst16: Option<&mut CudaSlice<u8>>,
11099 ncols: usize,
11100 nrows: usize,
11101 eps: f32,
11102 ) -> Result<(), Box<dyn std::error::Error>> {
11103 if Self::l2_v2_on(ncols) {
11104 let f = self.func("l2_norm_pp_v2_f32");
11105 let rows_per_block = 8u32; let cfg = LaunchConfig {
11107 grid_dim: ((nrows as u32).div_ceil(rows_per_block), 1, 1),
11108 block_dim: (256, 1, 1),
11109 shared_mem_bytes: 0,
11110 };
11111 let (nc, nr, e) = (ncols as i32, nrows as i32, eps);
11112 let d16: u64 = match dst16 {
11114 Some(d) => self.addr_u8(d),
11115 None => 0,
11116 };
11117 let __s_b = self.gpu.stream();
11118 let mut b = __s_b.launch_builder(&f);
11119 b.arg(x).arg(dst).arg(&d16).arg(&nc).arg(&nr).arg(&e);
11120 unsafe {
11121 b.launch(cfg)?;
11122 }
11123 return Ok(());
11124 }
11125 self.l2_norm(x, dst, ncols, nrows, eps)
11126 }
11127
11128 pub fn l2_norm(
11129 &self,
11130 x: &CudaSlice<f32>,
11131 dst: &mut CudaSlice<f32>,
11132 ncols: usize,
11133 nrows: usize,
11134 eps: f32,
11135 ) -> Result<(), Box<dyn std::error::Error>> {
11136 let f = self.func("l2_norm_f32");
11137 let cfg = LaunchConfig {
11138 grid_dim: (nrows as u32, 1, 1),
11139 block_dim: (256, 1, 1),
11140 shared_mem_bytes: 0,
11141 };
11142 let (nc, e) = (ncols as i32, eps);
11143 let __s_b = self.gpu.stream();
11144 let mut b = __s_b.launch_builder(&f);
11145 b.arg(x).arg(dst).arg(&nc).arg(&e);
11146 unsafe {
11147 b.launch(cfg)?;
11148 }
11149 Ok(())
11150 }
11151
11152 pub fn l2_norm_decode(
11158 &self,
11159 x: &CudaSlice<f32>,
11160 dst: &mut CudaSlice<f32>,
11161 ncols: usize,
11162 nrows: usize,
11163 eps: f32,
11164 ) -> Result<(), Box<dyn std::error::Error>> {
11165 let f = self.func("l2_norm_f32");
11166 let cfg = LaunchConfig {
11167 grid_dim: (nrows as u32, 1, 1),
11168 block_dim: (32, 1, 1),
11169 shared_mem_bytes: 0,
11170 };
11171 let (nc, e) = (ncols as i32, eps);
11172 let __s_b = self.gpu.stream();
11173 let mut b = __s_b.launch_builder(&f);
11174 b.arg(x).arg(dst).arg(&nc).arg(&e);
11175 unsafe {
11176 b.launch(cfg)?;
11177 }
11178 Ok(())
11179 }
11180
11181 pub fn rope_neox(
11183 &self,
11184 x: &mut CudaSlice<f32>,
11185 pos: &CudaSlice<i32>,
11186 head_dim: usize,
11187 n_dims: usize,
11188 n_heads: usize,
11189 n_tokens: usize,
11190 freq_base: f32,
11191 freq_scale: f32,
11192 ) -> Result<(), Box<dyn std::error::Error>> {
11193 let f = self.func("rope_neox_f32");
11194 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
11195 let grid = (n_heads * n_tokens) as u32;
11196 let cfg = LaunchConfig {
11197 grid_dim: (grid, 1, 1),
11198 block_dim: ((head_dim / 2) as u32, 1, 1),
11199 shared_mem_bytes: 0,
11200 };
11201 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
11202 let __s_b = self.gpu.stream();
11203 let mut b = __s_b.launch_builder(&f);
11204 b.arg(x)
11205 .arg(pos)
11206 .arg(&hd)
11207 .arg(&nd)
11208 .arg(&nh)
11209 .arg(&theta_scale)
11210 .arg(&freq_scale);
11211 unsafe {
11212 b.launch(cfg)?;
11213 }
11214 Ok(())
11215 }
11216
11217 pub fn rope_neox_ff(
11219 &self,
11220 x: &mut CudaSlice<f32>,
11221 pos: &CudaSlice<i32>,
11222 head_dim: usize,
11223 n_dims: usize,
11224 n_heads: usize,
11225 n_tokens: usize,
11226 freq_base: f32,
11227 freq_scale: f32,
11228 ff: &CudaSlice<f32>,
11229 ) -> Result<(), Box<dyn std::error::Error>> {
11230 let f = self.func("rope_neox_ff_f32");
11231 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
11232 let grid = (n_heads * n_tokens) as u32;
11233 let cfg = LaunchConfig {
11234 grid_dim: (grid, 1, 1),
11235 block_dim: ((head_dim / 2) as u32, 1, 1),
11236 shared_mem_bytes: 0,
11237 };
11238 let (hd, nd, nh) = (head_dim as i32, n_dims as i32, n_heads as i32);
11239 let __s_b = self.gpu.stream();
11240 let mut b = __s_b.launch_builder(&f);
11241 b.arg(x)
11242 .arg(pos)
11243 .arg(&hd)
11244 .arg(&nd)
11245 .arg(&nh)
11246 .arg(&theta_scale)
11247 .arg(&freq_scale)
11248 .arg(ff);
11249 unsafe {
11250 b.launch(cfg)?;
11251 }
11252 Ok(())
11253 }
11254
11255 #[allow(clippy::too_many_arguments)]
11257 pub fn rope_neox2(
11258 &self,
11259 q: &mut CudaSlice<f32>,
11260 k: &mut CudaSlice<f32>,
11261 pos: &CudaSlice<i32>,
11262 head_dim: usize,
11263 n_dims: usize,
11264 nh_q: usize,
11265 nh_k: usize,
11266 n_tokens: usize,
11267 freq_base: f32,
11268 freq_scale: f32,
11269 ff: Option<&CudaSlice<f32>>,
11270 ) -> Result<(), Box<dyn std::error::Error>> {
11271 let f = self.func("rope_neox2_f32");
11272 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
11273 let grid = ((nh_q + nh_k) * n_tokens) as u32;
11274 let cfg = LaunchConfig {
11275 grid_dim: (grid, 1, 1),
11276 block_dim: ((head_dim / 2) as u32, 1, 1),
11277 shared_mem_bytes: 0,
11278 };
11279 let (hd, nd, nq, nk, nt) = (
11280 head_dim as i32,
11281 n_dims as i32,
11282 nh_q as i32,
11283 nh_k as i32,
11284 n_tokens as i32,
11285 );
11286 let __s_b = self.gpu.stream();
11287 let mut b = __s_b.launch_builder(&f);
11288 b.arg(q)
11289 .arg(k)
11290 .arg(pos)
11291 .arg(&hd)
11292 .arg(&nd)
11293 .arg(&nq)
11294 .arg(&nk)
11295 .arg(&nt)
11296 .arg(&theta_scale)
11297 .arg(&freq_scale);
11298 match ff {
11299 Some(ffv) => {
11300 b.arg(ffv);
11301 unsafe {
11302 b.launch(cfg)?;
11303 }
11304 }
11305 None => {
11306 let null: u64 = 0;
11307 b.arg(&null);
11308 unsafe {
11309 b.launch(cfg)?;
11310 }
11311 }
11312 }
11313 Ok(())
11314 }
11315
11316 pub fn gelu_tanh_mul(
11318 &self,
11319 gate: &CudaSlice<f32>,
11320 up: &CudaSlice<f32>,
11321 dst: &mut CudaSlice<f32>,
11322 n: usize,
11323 ) -> Result<(), Box<dyn std::error::Error>> {
11324 let f = self.func("gelu_tanh_mul_f32");
11325 let cfg = LaunchConfig::for_num_elems(n as u32);
11326 let ni = n as i32;
11327 let __s_b = self.gpu.stream();
11328 let mut b = __s_b.launch_builder(&f);
11329 b.arg(gate).arg(up).arg(dst).arg(&ni);
11330 unsafe {
11331 b.launch(cfg)?;
11332 }
11333 Ok(())
11334 }
11335
11336 pub fn silu_mul(
11337 &self,
11338 gate: &CudaSlice<f32>,
11339 up: &CudaSlice<f32>,
11340 dst: &mut CudaSlice<f32>,
11341 n: usize,
11342 ) -> Result<(), Box<dyn std::error::Error>> {
11343 let f = self.func("silu_mul_f32");
11344 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
11346 let ni = n as i32;
11347 let __s_b = self.gpu.stream();
11348 let mut b = __s_b.launch_builder(&f);
11349 b.arg(gate).arg(up).arg(dst).arg(&ni);
11350 unsafe {
11351 b.launch(cfg)?;
11352 }
11353 Ok(())
11354 }
11355
11356 pub fn silu_mul_host_expf(
11358 &self,
11359 gate: &CudaSlice<f32>,
11360 up: &CudaSlice<f32>,
11361 dst: &mut CudaSlice<f32>,
11362 n: usize,
11363 ) -> Result<(), Box<dyn std::error::Error>> {
11364 let f = self.func("silu_mul_host_expf_f32");
11365 let cfg = LaunchConfig::for_num_elems(n as u32);
11366 let ni = n as i32;
11367 let __s_b = self.gpu.stream();
11368 let mut b = __s_b.launch_builder(&f);
11369 b.arg(gate).arg(up).arg(dst).arg(&ni);
11370 unsafe {
11371 b.launch(cfg)?;
11372 }
11373 Ok(())
11374 }
11375
11376 pub fn silu_clamped_mul_host_expf(
11378 &self,
11379 gate: &CudaSlice<f32>,
11380 up: &CudaSlice<f32>,
11381 limit: f32,
11382 dst: &mut CudaSlice<f32>,
11383 n: usize,
11384 ) -> Result<(), Box<dyn std::error::Error>> {
11385 if !limit.is_finite() || limit <= 0.0 {
11386 return Err(
11387 format!("Step routed-expert clamp limit must be positive, got {limit}").into(),
11388 );
11389 }
11390 let f = self.func("silu_clamped_mul_host_expf_f32");
11391 let cfg = LaunchConfig::for_num_elems(n as u32);
11392 let ni = n as i32;
11393 let __s_b = self.gpu.stream();
11394 let mut b = __s_b.launch_builder(&f);
11395 b.arg(gate).arg(up).arg(&limit).arg(dst).arg(&ni);
11396 unsafe {
11397 b.launch(cfg)?;
11398 }
11399 Ok(())
11400 }
11401
11402 pub fn silu_mul_f16out(
11405 &self,
11406 gate: &CudaSlice<f32>,
11407 up: &CudaSlice<f32>,
11408 dst: &mut CudaSlice<f32>,
11409 dst16: &mut CudaSlice<u8>,
11410 n: usize,
11411 ) -> Result<(), Box<dyn std::error::Error>> {
11412 let f = self.func("silu_mul_f16out_f32");
11413 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
11414 let ni = n as i32;
11415 let __s_b = self.gpu.stream();
11416 let mut b = __s_b.launch_builder(&f);
11417 b.arg(gate).arg(up).arg(dst).arg(dst16).arg(&ni);
11418 unsafe {
11419 b.launch(cfg)?;
11420 }
11421 Ok(())
11422 }
11423
11424 pub fn silu_mul_scaled(
11431 &self,
11432 gate: &CudaSlice<f32>,
11433 up: &CudaSlice<f32>,
11434 gs: f32,
11435 us: f32,
11436 dst: &mut CudaSlice<f32>,
11437 n: usize,
11438 ) -> Result<(), Box<dyn std::error::Error>> {
11439 let f = self.func("silu_mul_scaled_f32");
11440 let cfg = LaunchConfig::for_num_elems(n as u32);
11441 let ni = n as i32;
11442 let (gsf, usf) = (gs, us);
11443 let __s_b = self.gpu.stream();
11444 let mut b = __s_b.launch_builder(&f);
11445 b.arg(gate).arg(up).arg(&gsf).arg(&usf).arg(dst).arg(&ni);
11446 unsafe {
11447 b.launch(cfg)?;
11448 }
11449 Ok(())
11450 }
11451
11452 #[allow(clippy::too_many_arguments)]
11456 pub fn swigluoai_mul_scaled(
11457 &self,
11458 gate: &CudaSlice<f32>,
11459 up: &CudaSlice<f32>,
11460 gs: f32,
11461 us: f32,
11462 alpha: f32,
11463 limit: f32,
11464 dst: &mut CudaSlice<f32>,
11465 n: usize,
11466 ) -> Result<(), Box<dyn std::error::Error>> {
11467 let f = self.func("swigluoai_mul_scaled_f32");
11468 let cfg = LaunchConfig::for_num_elems(n as u32);
11469 let ni = n as i32;
11470 let __s_b = self.gpu.stream();
11471 let mut b = __s_b.launch_builder(&f);
11472 b.arg(gate)
11473 .arg(up)
11474 .arg(&gs)
11475 .arg(&us)
11476 .arg(&alpha)
11477 .arg(&limit)
11478 .arg(dst)
11479 .arg(&ni);
11480 unsafe {
11481 b.launch(cfg)?;
11482 }
11483 Ok(())
11484 }
11485
11486 pub fn silu_mul_scaled_q8_1(
11494 &self,
11495 gate: &CudaSlice<f32>,
11496 up: &CudaSlice<f32>,
11497 gs: f32,
11498 us: f32,
11499 n: usize,
11500 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
11501 let f = self.func("silu_mul_scaled_q8_1");
11502 let nblk = n / 32;
11503 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);
11507 let (gsf, usf, ni) = (gs, us, n as i32);
11508 let __s_b = self.gpu.stream();
11509 let mut b = __s_b.launch_builder(&f);
11510 b.arg(gate)
11511 .arg(up)
11512 .arg(&gsf)
11513 .arg(&usf)
11514 .arg(&mut aq)
11515 .arg(&mut ad)
11516 .arg(&ni);
11517 unsafe {
11518 b.launch(cfg)?;
11519 }
11520 Ok((aq, ad))
11521 }
11522
11523 pub fn add(
11524 &self,
11525 a: &CudaSlice<f32>,
11526 b_in: &CudaSlice<f32>,
11527 dst: &mut CudaSlice<f32>,
11528 n: usize,
11529 ) -> Result<(), Box<dyn std::error::Error>> {
11530 let f = self.func("add_f32");
11531 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
11533 let ni = n as i32;
11534 let __s_bld = self.gpu.stream();
11535 let mut bld = __s_bld.launch_builder(&f);
11536 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
11537 unsafe {
11538 bld.launch(cfg)?;
11539 }
11540 Ok(())
11541 }
11542
11543 pub fn mul(
11544 &self,
11545 a: &CudaSlice<f32>,
11546 b_in: &CudaSlice<f32>,
11547 dst: &mut CudaSlice<f32>,
11548 n: usize,
11549 ) -> Result<(), Box<dyn std::error::Error>> {
11550 let f = self.func("mul_f32");
11551 let cfg = LaunchConfig::for_num_elems(n as u32);
11552 let ni = n as i32;
11553 let __s_bld = self.gpu.stream();
11554 let mut bld = __s_bld.launch_builder(&f);
11555 bld.arg(a).arg(b_in).arg(dst).arg(&ni);
11556 unsafe {
11557 bld.launch(cfg)?;
11558 }
11559 Ok(())
11560 }
11561
11562 pub fn matmul(
11565 &self,
11566 w: &crate::model::GpuTensor,
11567 x: &CudaSlice<f32>,
11568 m: usize,
11569 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11570 use crate::model::GpuTensor;
11571 let in_f = w.in_features();
11572 let out_f = w.out_features();
11573 #[allow(non_snake_case)]
11581 let GEMM_M_THRESHOLD = if self.verify_exact_on() {
11584 usize::MAX
11585 } else {
11586 16usize
11587 };
11588
11589 const GEMM_MIN_OUT_F: usize = 128; if m >= GEMM_M_THRESHOLD {
11614 if let Some(y) = self.try_fp8_gemm(w, x, m)? {
11615 return Ok(y);
11616 }
11617 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
11624 return Ok(y);
11625 }
11626 if let Some(y) = self.try_f16_gemm(w, x, m)? {
11629 return Ok(y);
11630 }
11631 }
11632 if let GpuTensor::Quant { qtype, .. } = w {
11647 if *qtype == QT_F8_E4M3_BLK {
11648 if m >= GEMM_M_THRESHOLD {
11649 if let Some(y) = self.try_e4m3_blk_prefill(w, x, m)? {
11650 return Ok(y);
11651 }
11652 }
11653 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11654 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
11655 return Ok(y);
11656 }
11657 }
11658 }
11659 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.mmq_supports(w) {
11660 return self.qmatvec_mmq(w, x, m);
11661 }
11662 if m >= GEMM_M_THRESHOLD && out_f >= GEMM_MIN_OUT_F && self.gemm_supports(w) {
11663 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11664 return self.qmatvec_gemm(w, &aq, &ad, m);
11665 }
11666 if m >= GEMM_M_THRESHOLD {
11669 if let Some(y) = self.try_fp4_gemm(w, x, m, in_f, out_f)? {
11670 return Ok(y);
11671 }
11672 }
11673 let fast = std::env::var("MEMRA_FAST").as_deref() != Ok("0");
11677 if m == 1 && fast {
11682 if let GpuTensor::Quant {
11683 bytes,
11684 qtype,
11685 row_bytes,
11686 rp,
11687 rp4,
11688 scale,
11689 ..
11690 } = w
11691 {
11692 if self.mmvq_supports(*qtype) {
11693 let (bytes, rp) = match rp4 {
11697 Some(m4) => (m4, true),
11698 None => (bytes, *rp),
11699 };
11700 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11701 return self.qmatvec_mmvq(
11702 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, rp,
11703 );
11704 }
11705 }
11706 }
11707 if (2..=16).contains(&m)
11723 && fast
11724 && std::env::var("MEMRA_NO_BATCHED").is_err()
11725 && (m <= 4 || Self::b8_enabled())
11726 {
11727 let m_ok = m <= 8
11737 || matches!(w, GpuTensor::Quant { qtype, .. }
11738 if *qtype == QT_Q4_0 || *qtype == QT_Q6_K || *qtype == QT_F8_E4M3
11739 || *qtype == QT_NVFP4 || *qtype == QT_Q4_K || *qtype == QT_Q5_K || *qtype == QT_Q8_0);
11740 if m_ok {
11741 if let GpuTensor::Quant {
11742 bytes,
11743 qtype,
11744 row_bytes,
11745 rp,
11746 rp4,
11747 ..
11748 } = w
11749 {
11750 if self.batched_supports(*qtype) && self.mmvq_supports(*qtype) {
11751 let (bytes, rp) = match rp4 {
11752 Some(m4) => (m4, true),
11753 None => (bytes, *rp),
11754 };
11755 let mcols = Self::batched_mcols(m);
11756 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11757 let mut y = self.qmatvec_mmvq_batched(
11758 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, mcols, 1.0, rp,
11759 )?;
11760 if let GpuTensor::Quant { scale, .. } = w {
11761 if *scale != 1.0 {
11762 self.scale_inplace(&mut y, *scale, m * out_f)?;
11763 }
11764 }
11765 return Ok(y);
11766 }
11767 }
11768 }
11769 }
11770 if fast {
11776 if let GpuTensor::Quant {
11777 bytes,
11778 qtype,
11779 row_bytes,
11780 scale,
11781 ..
11782 } = w
11783 {
11784 if *qtype == QT_F8_E4M3 {
11785 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
11786 return self.qmatvec_mmvq(
11787 bytes, &aq, &ad, m, in_f, out_f, *qtype, *row_bytes, *scale, false,
11788 );
11789 }
11790 }
11791 }
11792 let mut y = match w {
11793 GpuTensor::Quant {
11794 bytes,
11795 qtype,
11796 row_bytes,
11797 ..
11798 } if fast && *qtype == QT_Q8_0 => {
11799 self.qmatvec_q8_0_fast(bytes, x, m, in_f, out_f, *row_bytes)?
11800 }
11801 GpuTensor::Quant {
11802 bytes,
11803 qtype,
11804 row_bytes,
11805 ..
11806 } if fast && *qtype == QT_Q4_K => {
11807 self.qmatvec_q4_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
11808 }
11809 GpuTensor::Quant {
11810 bytes,
11811 qtype,
11812 row_bytes,
11813 ..
11814 } if fast && *qtype == QT_Q6_K => {
11815 self.qmatvec_q6_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
11816 }
11817 GpuTensor::Quant {
11818 bytes,
11819 qtype,
11820 row_bytes,
11821 ..
11822 } if fast && *qtype == QT_Q5_K => {
11823 self.qmatvec_q5_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
11824 }
11825 GpuTensor::Quant {
11826 bytes,
11827 qtype,
11828 row_bytes,
11829 ..
11830 } if fast && *qtype == QT_Q3_K => {
11831 self.qmatvec_q3_K_fast(bytes, x, m, in_f, out_f, *row_bytes)?
11832 }
11833 GpuTensor::Quant {
11834 bytes,
11835 qtype,
11836 row_bytes,
11837 rp,
11838 ..
11839 } if fast && *qtype == QT_NVFP4 => self.qmatvec_dp4a_named(
11840 if *rp {
11841 "qmatvec_nvfp4_dp4a_rp"
11842 } else {
11843 "qmatvec_nvfp4_dp4a"
11844 },
11845 &bytes.slice(0..bytes.len()),
11846 x,
11847 m,
11848 in_f,
11849 out_f,
11850 *row_bytes,
11851 )?,
11852 GpuTensor::Quant {
11856 bytes,
11857 qtype,
11858 row_bytes,
11859 ..
11860 } if fast && *qtype == QT_IQ4_XS && Self::iq_fast_enabled() => {
11861 self.qmatvec_iq4_XS_fast(bytes, x, m, in_f, out_f, *row_bytes)?
11862 }
11863 GpuTensor::Quant {
11868 bytes,
11869 qtype,
11870 row_bytes,
11871 rp,
11872 ..
11873 } =>
11874 {
11877 self.qmatvec(
11878 bytes,
11879 x,
11880 m,
11881 in_f,
11882 out_f,
11883 if *rp && *qtype == QT_NVFP4 {
11884 QT_NVFP4_RP
11885 } else {
11886 *qtype
11887 },
11888 *row_bytes,
11889 )?
11890 }
11891 GpuTensor::Float { data, .. } => self.linear(x, data, m, in_f, out_f)?,
11892 GpuTensor::FloatBf16 { data, .. } => {
11895 self.linear_bf16_chunked(x, data, m, in_f, out_f, false, None)?
11896 }
11897 };
11898 if let GpuTensor::Quant { scale, .. } = w {
11900 if *scale != 1.0 {
11901 self.scale_inplace(&mut y, *scale, m * out_f)?;
11902 }
11903 }
11904 Ok(y)
11905 }
11906
11907 pub fn stage_a_raw_needed() -> bool {
11917 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
11918 *ON.get_or_init(|| std::env::var("MEMRA_FAST").as_deref() == Ok("0"))
11919 }
11920
11921 pub fn uses_q8_1_fast(&self, w: &crate::model::GpuTensor) -> bool {
11924 use crate::model::GpuTensor;
11925 if std::env::var("MEMRA_FAST").as_deref() == Ok("0") {
11926 return false;
11927 }
11928 match w {
11929 GpuTensor::Quant { qtype, .. } => {
11936 matches!(
11937 *qtype,
11938 QT_Q8_0
11939 | QT_Q4_K
11940 | QT_Q6_K
11941 | QT_Q5_K
11942 | QT_Q3_K
11943 | QT_NVFP4
11944 | QT_F8_E4M3
11945 | QT_F8_E4M3_BLK
11946 | QT_Q4_0
11947 ) || (*qtype == QT_IQ4_XS && Self::iq_fast_enabled())
11948 }
11949 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
11950 }
11951 }
11952
11953 pub fn matmul_pre(
11958 &self,
11959 w: &crate::model::GpuTensor,
11960 aq: &CudaSlice<i8>,
11961 ad: &CudaSlice<f32>,
11962 x_fallback: &CudaSlice<f32>,
11963 m: usize,
11964 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
11965 use crate::model::GpuTensor;
11966 let x_raw_ok = x_fallback.len() >= m * w.in_features();
11972 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
11975 if let Some(y) = self.try_fp8_gemm(w, x_fallback, m)? {
11976 return Ok(y);
11977 }
11978 if let Some(y) = self.try_fp8_blk_mmq(w, x_fallback, m)? {
11981 return Ok(y);
11982 }
11983 if let Some(y) = self.try_f16_gemm(w, x_fallback, m)? {
11985 return Ok(y);
11986 }
11987 }
11988 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
11994 if let Some(y) = self.try_e4m3_blk_prefill(w, x_fallback, m)? {
11995 return Ok(y);
11996 }
11997 }
11998 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
11999 return Ok(y);
12000 }
12001 if m >= 16
12006 && w.out_features() >= 128
12007 && self.mmq_supports(w)
12008 && !self.verify_exact_on()
12009 && x_raw_ok
12010 {
12011 return self.qmatvec_mmq(w, x_fallback, m);
12012 }
12013 if m >= 16 && x_raw_ok && !self.verify_exact_on() {
12016 if let Some(y) =
12017 self.try_fp4_gemm(w, x_fallback, m, w.in_features(), w.out_features())?
12018 {
12019 return Ok(y);
12020 }
12021 }
12022 if m >= 16 && self.gemm_supports(w) && !self.verify_exact_on() {
12025 return self.qmatvec_gemm(w, aq, ad, m);
12026 }
12027 if !self.uses_q8_1_fast(w) {
12046 if !x_raw_ok {
12047 return Err(format!(
12048 "matmul_pre: q8_1-fast is off for this weight but x_fallback holds {} f32 \
12049 (need m*in_f = {}*{} = {}). This call site pre-quantized its activation and \
12050 dropped the f32, so there is nothing to fall back to — pass the real f32 \
12051 activation (see Engine::rms_norm_decode, which is bit-identical to \
12052 rms_norm_q8_1's reduction) or keep the weight on the q8_1 path.",
12053 x_fallback.len(),
12054 m,
12055 w.in_features(),
12056 m * w.in_features()
12057 )
12058 .into());
12059 }
12060 return self.matmul(w, x_fallback, m);
12061 }
12062 let in_f = w.in_features();
12063 let out_f = w.out_features();
12064 let (bytes, qtype, row_bytes, scale, rp) = match w {
12065 GpuTensor::Quant {
12066 bytes,
12067 qtype,
12068 row_bytes,
12069 scale,
12070 rp,
12071 ..
12072 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12073 _ => unreachable!("uses_q8_1_fast guaranteed Quant"),
12074 };
12075 let (mbytes, mrp) = match w {
12078 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12079 _ => (bytes, rp),
12080 };
12081 if m == 1 && self.mmvq_supports(qtype) {
12085 return self.qmatvec_mmvq(mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, mrp);
12086 }
12087 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
12100 && std::env::var("MEMRA_NO_BATCHED").is_err()
12101 && (m <= 4 || Self::b8_enabled())
12102 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_NVFP4
12106 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_F8_E4M3 || qtype == QT_Q8_0)
12107 {
12108 let mcols = Self::batched_mcols(m);
12109 return self.qmatvec_mmvq_batched(
12110 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, mrp,
12111 );
12112 }
12113 if qtype == QT_F8_E4M3 || qtype == QT_Q4_0 {
12119 let (b2, r2) = if qtype == QT_Q4_0 {
12120 (mbytes, mrp)
12121 } else {
12122 (bytes, rp)
12123 };
12124 return self.qmatvec_mmvq(b2, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, r2);
12125 }
12126 let name = match qtype {
12127 QT_Q8_0 => "qmatvec_q8_0_dp4a",
12128 QT_Q4_K => "qmatvec_q4_K_dp4a",
12129 QT_Q6_K => "qmatvec_q6_K_dp4a",
12130 QT_Q5_K => "qmatvec_q5_K_dp4a",
12131 QT_Q3_K => "qmatvec_q3_K_dp4a",
12132 QT_NVFP4 => {
12133 if rp {
12134 "qmatvec_nvfp4_dp4a_rp"
12135 } else {
12136 "qmatvec_nvfp4_dp4a"
12137 }
12138 }
12139 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
12140 _ => unreachable!(),
12141 };
12142 let f = self.func(name);
12143 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
12145 grid_dim: (out_f as u32, m as u32, 1),
12146 block_dim: (128, 1, 1),
12147 shared_mem_bytes: 0,
12148 };
12149 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12150 let __s_b = self.gpu.stream();
12151 let mut b = __s_b.launch_builder(&f);
12152 b.arg(bytes)
12153 .arg(aq)
12154 .arg(ad)
12155 .arg(&mut y)
12156 .arg(&inf)
12157 .arg(&outf)
12158 .arg(&mi)
12159 .arg(&rb);
12160 unsafe {
12161 b.launch(cfg)?;
12162 }
12163 if scale != 1.0 {
12164 self.scale_inplace(&mut y, scale, m * out_f)?;
12165 }
12166 Ok(y)
12167 }
12168
12169 pub fn matmul_decode_exact(
12177 &self,
12178 w: &crate::model::GpuTensor,
12179 x: &CudaSlice<f32>,
12180 m: usize,
12181 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12182 use crate::model::GpuTensor;
12183 if let GpuTensor::Float { data, .. } = w {
12191 return self.linear_decode_exact(x, data, m, w.in_features(), w.out_features());
12192 }
12193 if let GpuTensor::FloatBf16 { data, .. } = w {
12196 let (in_f, out_f) = (w.in_features(), w.out_features());
12197 return self.linear_bf16_chunked(x, data, m, in_f, out_f, true, None);
12198 }
12199 if !self.uses_q8_1_fast(w) {
12200 return self.matmul(w, x, m);
12201 }
12202 let in_f = w.in_features();
12203 let out_f = w.out_features();
12204 let (bytes, qtype, row_bytes, scale, rp) = match w {
12205 GpuTensor::Quant {
12206 bytes,
12207 qtype,
12208 row_bytes,
12209 scale,
12210 rp,
12211 ..
12212 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12213 _ => return self.matmul(w, x, m),
12214 };
12215 let (bytes, rp) = match w {
12218 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12219 _ => (bytes, rp),
12220 };
12221 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12222 if let Some(y) = self.try_e4m3_blk_pre(w, &aq, &ad, m)? {
12226 return Ok(y);
12227 }
12228 if (2..=16).contains(&m) && self.batched_supports(qtype) && self.mmvq_supports(qtype)
12237 && std::env::var("MEMRA_NO_BATCHED").is_err()
12238 && (m <= 4 || Self::b8_enabled())
12239 && (m <= 8 || qtype == QT_Q4_0 || qtype == QT_Q6_K || qtype == QT_F8_E4M3
12242 || qtype == QT_NVFP4 || qtype == QT_Q4_K || qtype == QT_Q5_K || qtype == QT_Q8_0)
12243 {
12244 let mcols = Self::batched_mcols(m);
12245 return self.qmatvec_mmvq_batched(
12246 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
12247 );
12248 }
12249 if self.mmvq_supports(qtype) {
12250 return self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
12253 }
12254 self.matmul_pre(w, &aq, &ad, x, m)
12257 }
12258
12259 pub fn matmul_decode_exact_pre(
12269 &self,
12270 w: &crate::model::GpuTensor,
12271 aq: &CudaSlice<i8>,
12272 ad: &CudaSlice<f32>,
12273 m: usize,
12274 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
12275 use crate::model::GpuTensor;
12276 debug_assert!(
12277 self.uses_q8_1_fast(w),
12278 "matmul_decode_exact_pre: caller must guarantee q8_1-fast"
12279 );
12280 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
12282 return Ok(y);
12283 }
12284 let in_f = w.in_features();
12285 let out_f = w.out_features();
12286 let (bytes, qtype, row_bytes, scale, rp) = match w {
12287 GpuTensor::Quant {
12288 bytes,
12289 qtype,
12290 row_bytes,
12291 scale,
12292 rp,
12293 ..
12294 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12295 _ => {
12296 return Err(
12297 "matmul_decode_exact_pre: Quant tensor required (q8_1-fast contract)".into(),
12298 );
12299 }
12300 };
12301 let (bytes, rp) = match w {
12303 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
12304 _ => (bytes, rp),
12305 };
12306 if (2..=16).contains(&m)
12308 && self.batched_supports(qtype)
12309 && self.mmvq_supports(qtype)
12310 && std::env::var("MEMRA_NO_BATCHED").is_err()
12311 && (m <= 4 || Self::b8_enabled())
12312 && (m <= 8
12313 || qtype == QT_Q4_0
12314 || qtype == QT_Q6_K
12315 || qtype == QT_F8_E4M3
12316 || qtype == QT_NVFP4
12317 || qtype == QT_Q4_K
12318 || qtype == QT_Q5_K
12319 || qtype == QT_Q8_0)
12320 {
12321 let mcols = Self::batched_mcols(m);
12322 return self.qmatvec_mmvq_batched(
12323 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, mcols, scale, rp,
12324 );
12325 }
12326 if self.mmvq_supports(qtype) {
12327 return self.qmatvec_mmvq(bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp);
12328 }
12329 let x0 = self.zeros(0)?;
12332 self.matmul_pre(w, aq, ad, &x0, m)
12333 }
12334
12335 pub fn matmul_decode_exact_dual_pre(
12344 &self,
12345 w0: &crate::model::GpuTensor,
12346 w1: &crate::model::GpuTensor,
12347 aq: &CudaSlice<i8>,
12348 ad: &CudaSlice<f32>,
12349 m: usize,
12350 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
12351 {
12352 use crate::model::GpuTensor;
12353 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12354 let on = *ON.get_or_init(|| {
12355 std::env::var("MEMRA_SPEC_DUAL_T")
12356 .map(|v| v != "0")
12357 .unwrap_or(true)
12358 });
12359 if !on
12360 || !(2..=7).contains(&m)
12361 || std::env::var("MEMRA_NO_BATCHED").is_ok()
12362 || !self.uses_q8_1_fast(w0)
12363 || !self.uses_q8_1_fast(w1)
12364 {
12365 return Ok(None);
12366 }
12367 if !self.mmvq_supports(QT_NVFP4) {
12372 return Ok(None);
12373 }
12374 let (in_f, out_f) = (w0.in_features(), w0.out_features());
12375 if w1.in_features() != in_f || w1.out_features() != out_f {
12376 return Ok(None);
12377 }
12378 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
12379 (
12380 GpuTensor::Quant {
12381 bytes: b0,
12382 qtype: q0,
12383 row_bytes: rb0,
12384 scale: s0,
12385 rp: rp0,
12386 rp4: None,
12387 ..
12388 },
12389 GpuTensor::Quant {
12390 bytes: b1,
12391 qtype: q1,
12392 row_bytes: rb1,
12393 scale: s1,
12394 rp: rp1,
12395 rp4: None,
12396 ..
12397 },
12398 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
12399 (b0, b1, *rb0, *s0, *s1, *rp0)
12400 }
12401 _ => return Ok(None),
12402 };
12403 if m > 4 && !(rp && Self::b8_enabled() && std::env::var("MEMRA_B567").as_deref() != Ok("0"))
12406 {
12407 return Ok(None);
12408 }
12409 let (y0, y1) =
12410 self.qmatvec_batched_dual_raw(b0, b1, aq, ad, m, in_f, out_f, row_bytes, rp)?;
12411 Ok(Some(((y0, s0), (y1, s1))))
12412 }
12413
12414 pub fn matmul_decode_exact_group4_pre(
12426 &self,
12427 ws: [&crate::model::GpuTensor; 4],
12428 aq: &CudaSlice<i8>,
12429 ad: &CudaSlice<f32>,
12430 m: usize,
12431 ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
12432 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12433 let on = *ON.get_or_init(|| {
12434 std::env::var("MEMRA_TK_GDN_GROUP")
12435 .map(|v| v != "0")
12436 .unwrap_or(true)
12437 });
12438 self.matmul_decode_exact_group_pre(&ws, aq, ad, m, on, "GDN group4")
12439 }
12440
12441 pub fn matmul_decode_exact_group3_pre(
12446 &self,
12447 ws: [&crate::model::GpuTensor; 3],
12448 aq: &CudaSlice<i8>,
12449 ad: &CudaSlice<f32>,
12450 m: usize,
12451 ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
12452 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12453 let on = *ON.get_or_init(|| {
12454 std::env::var("MEMRA_TK_FA_GROUP")
12455 .map(|v| v != "0")
12456 .unwrap_or(true)
12457 });
12458 self.matmul_decode_exact_group_pre(&ws, aq, ad, m, on, "FA group3")
12459 }
12460
12461 fn matmul_decode_exact_group_pre(
12465 &self,
12466 ws: &[&crate::model::GpuTensor],
12467 aq: &CudaSlice<i8>,
12468 ad: &CudaSlice<f32>,
12469 m: usize,
12470 on: bool,
12471 tag: &'static str,
12472 ) -> Result<Option<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
12473 use crate::model::GpuTensor;
12474 if !on
12475 || !(2..=16).contains(&m)
12476 || std::env::var("MEMRA_NO_BATCHED").is_ok()
12477 || (m > 4 && !Self::b8_enabled())
12478 || !self.mmvq_supports(QT_NVFP4)
12479 || !self.batched_supports(QT_NVFP4)
12480 {
12481 return Ok(None);
12482 }
12483 let in_f = ws[0].in_features();
12484 let mut parts: Vec<(&CudaSlice<u8>, usize, f32)> = Vec::with_capacity(4);
12485 for w in ws {
12486 if !self.uses_q8_1_fast(w) || w.in_features() != in_f {
12487 return Ok(None);
12488 }
12489 match w {
12490 GpuTensor::Quant {
12491 bytes,
12492 qtype,
12493 scale,
12494 rp: true,
12495 rp4: None,
12496 ..
12497 } if *qtype == QT_NVFP4 && w.out_features() % 8 == 0 => {
12498 parts.push((bytes, w.out_features(), *scale));
12499 }
12500 _ => return Ok(None),
12501 }
12502 }
12503 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12505 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
12506 let mcols = if (5..=7).contains(&m) && b567 {
12507 m
12508 } else {
12509 Self::batched_mcols(m)
12510 };
12511 let kname: &'static str = match mcols {
12512 2 => "qmatvec_nvfp4_mmvq_group4_b2_rp",
12513 4 => "qmatvec_nvfp4_mmvq_group4_b4_rp",
12514 5 => "qmatvec_nvfp4_mmvq_group4_b5_rp",
12515 6 => "qmatvec_nvfp4_mmvq_group4_b6_rp",
12516 7 => "qmatvec_nvfp4_mmvq_group4_b7_rp",
12517 8 => "qmatvec_nvfp4_mmvq_group4_b8_rp",
12518 16 => "qmatvec_nvfp4_mmvq_group4_b16_rp",
12519 _ => return Ok(None),
12520 };
12521 if std::env::var("MEMRA_DEBUG").is_ok() {
12524 use std::sync::Mutex;
12525 static SEEN: Mutex<Vec<&'static str>> = Mutex::new(Vec::new());
12526 let mut seen = SEEN.lock().unwrap();
12527 if !seen.contains(&tag) {
12528 seen.push(tag);
12529 eprintln!("[memra] {tag} batched ENGAGED (m={m})");
12530 }
12531 }
12532 const ROWS_PER_BLOCK: u32 = 4; let rows_per_block = ROWS_PER_BLOCK * 2; let total: usize = parts.iter().map(|p| p.1).sum();
12535 let three = parts.len() == 3;
12536 let mut y0 = self.alloc_uninit::<f32>(m * parts[0].1)?;
12537 let mut y1 = self.alloc_uninit::<f32>(m * parts[1].1)?;
12538 let mut y2 = self.alloc_uninit::<f32>(m * parts[2].1)?;
12539 let mut y3 = self.alloc_uninit::<f32>(if three { 1 } else { m * parts[3].1 })?;
12542 let cfg = LaunchConfig {
12543 grid_dim: ((total as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
12544 block_dim: (32, ROWS_PER_BLOCK, 1),
12545 shared_mem_bytes: 0,
12546 };
12547 let (inf, mi) = (in_f as i32, m as i32);
12548 let (n0, n1, n2) = (parts[0].1 as i32, parts[1].1 as i32, parts[2].1 as i32);
12549 let n3 = if three { 0i32 } else { parts[3].1 as i32 };
12550 let (s0, s1, s2) = (parts[0].2, parts[1].2, parts[2].2);
12551 let s3 = if three { 1.0f32 } else { parts[3].2 };
12552 let w3 = if three { parts[0].0 } else { parts[3].0 };
12553 let f = self.func(kname);
12554 let __s_b = self.gpu.stream();
12555 let mut b = __s_b.launch_builder(&f);
12556 b.arg(parts[0].0)
12557 .arg(parts[1].0)
12558 .arg(parts[2].0)
12559 .arg(w3)
12560 .arg(aq)
12561 .arg(ad)
12562 .arg(&mut y0)
12563 .arg(&mut y1)
12564 .arg(&mut y2)
12565 .arg(&mut y3)
12566 .arg(&inf)
12567 .arg(&n0)
12568 .arg(&n1)
12569 .arg(&n2)
12570 .arg(&n3)
12571 .arg(&mi)
12572 .arg(&s0)
12573 .arg(&s1)
12574 .arg(&s2)
12575 .arg(&s3);
12576 unsafe {
12577 b.launch(cfg)?;
12578 }
12579 Ok(Some(if three {
12580 vec![y0, y1, y2]
12581 } else {
12582 vec![y0, y1, y2, y3]
12583 }))
12584 }
12585
12586 pub fn matmul_decode_exact_dual(
12602 &self,
12603 w0: &crate::model::GpuTensor,
12604 w1: &crate::model::GpuTensor,
12605 x: &CudaSlice<f32>,
12606 m: usize,
12607 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
12608 use crate::model::GpuTensor;
12609 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
12610 let on = *ON.get_or_init(|| {
12611 std::env::var("MEMRA_SPEC_DUAL_T")
12612 .map(|v| v != "0")
12613 .unwrap_or(true)
12614 });
12615 if !on
12616 || !(2..=4).contains(&m)
12617 || std::env::var("MEMRA_NO_BATCHED").is_ok()
12618 || !self.uses_q8_1_fast(w0)
12619 || !self.uses_q8_1_fast(w1)
12620 {
12621 return Ok(None);
12622 }
12623 if !self.mmvq_supports(QT_NVFP4) {
12628 return Ok(None);
12629 }
12630 let (in_f, out_f) = (w0.in_features(), w0.out_features());
12631 if w1.in_features() != in_f || w1.out_features() != out_f {
12632 return Ok(None);
12633 }
12634 let (b0, b1, row_bytes, s0, s1, rp) = match (w0, w1) {
12635 (
12636 GpuTensor::Quant {
12637 bytes: b0,
12638 qtype: q0,
12639 row_bytes: rb0,
12640 scale: s0,
12641 rp: rp0,
12642 rp4: None,
12643 ..
12644 },
12645 GpuTensor::Quant {
12646 bytes: b1,
12647 qtype: q1,
12648 row_bytes: rb1,
12649 scale: s1,
12650 rp: rp1,
12651 rp4: None,
12652 ..
12653 },
12654 ) if *q0 == QT_NVFP4 && *q1 == QT_NVFP4 && rb0 == rb1 && rp0 == rp1 => {
12655 (b0, b1, *rb0, *s0, *s1, *rp0)
12656 }
12657 _ => return Ok(None),
12658 };
12659 if std::env::var("MEMRA_DEBUG").is_ok() {
12662 static ONCE: std::sync::Once = std::sync::Once::new();
12663 ONCE.call_once(|| eprintln!("[memra] dual gate+up batched ENGAGED (m={m} rp={rp})"));
12664 }
12665 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
12666 let (y0, y1) =
12667 self.qmatvec_batched_dual_raw(b0, b1, &aq, &ad, m, in_f, out_f, row_bytes, rp)?;
12668 let mut y0 = y0;
12669 let mut y1 = y1;
12670 if s0 != 1.0 {
12671 self.scale_inplace(&mut y0, s0, m * out_f)?;
12672 }
12673 if s1 != 1.0 {
12674 self.scale_inplace(&mut y1, s1, m * out_f)?;
12675 }
12676 Ok(Some((y0, y1)))
12677 }
12678
12679 #[allow(clippy::too_many_arguments)]
12684 pub fn qmatvec_batched_dual_raw(
12685 &self,
12686 b0: &CudaSlice<u8>,
12687 b1: &CudaSlice<u8>,
12688 aq: &CudaSlice<i8>,
12689 ad: &CudaSlice<f32>,
12690 m: usize,
12691 in_f: usize,
12692 out_f: usize,
12693 row_bytes: usize,
12694 rp: bool,
12695 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
12696 const ROWS_PER_BLOCK: u32 = 4;
12697 let mcols = Self::batched_mcols(m);
12698 let tiny_rp1 = rp
12701 && mcols == 4
12702 && out_f <= 128
12703 && std::env::var("MEMRA_NVFP4_AUX_DUAL").as_deref() != Ok("0");
12704 let (name, rows_per_block) = if tiny_rp1 {
12705 ("qmatvec_nvfp4_mmvq_dual_b4_rp", ROWS_PER_BLOCK)
12706 } else {
12707 match (mcols, rp, m) {
12708 (2, false, _) => ("qmatvec_nvfp4_mmvq_dual_b2", ROWS_PER_BLOCK),
12709 (4, false, _) => ("qmatvec_nvfp4_mmvq_dual_b4_r2", ROWS_PER_BLOCK * 2),
12710 (2, true, _) => ("qmatvec_nvfp4_mmvq_dual_b2_rp", ROWS_PER_BLOCK),
12711 (4, true, _) => ("qmatvec_nvfp4_mmvq_dual_b4_rpr2", ROWS_PER_BLOCK * 2),
12712 (8, true, 5) => ("qmatvec_nvfp4_mmvq_dual_b5_rpr2", ROWS_PER_BLOCK * 2),
12713 (8, true, 6) => ("qmatvec_nvfp4_mmvq_dual_b6_rpr2", ROWS_PER_BLOCK * 2),
12714 (8, true, 7) => ("qmatvec_nvfp4_mmvq_dual_b7_rpr2", ROWS_PER_BLOCK * 2),
12715 _ => {
12716 return Err(
12717 format!("qmatvec_batched_dual_raw: no dual kernel for m {m}").into(),
12718 );
12719 }
12720 }
12721 };
12722 let f = self.func(name);
12723 let mut y0 = self.alloc_uninit::<f32>(m * out_f)?;
12724 let mut y1 = self.alloc_uninit::<f32>(m * out_f)?;
12725 let cfg = LaunchConfig {
12726 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
12727 block_dim: (32, ROWS_PER_BLOCK, 1),
12728 shared_mem_bytes: 0,
12729 };
12730 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
12731 let __s_b = self.gpu.stream();
12732 let mut b = __s_b.launch_builder(&f);
12733 b.arg(b0)
12734 .arg(b1)
12735 .arg(aq)
12736 .arg(ad)
12737 .arg(&mut y0)
12738 .arg(&mut y1)
12739 .arg(&inf)
12740 .arg(&outf)
12741 .arg(&mi)
12742 .arg(&rb);
12743 unsafe {
12744 b.launch(cfg)?;
12745 }
12746 Ok((y0, y1))
12747 }
12748
12749 pub fn matmul_pre_dual_noscale(
12761 &self,
12762 w0: &crate::model::GpuTensor,
12763 w1: &crate::model::GpuTensor,
12764 aq: &CudaSlice<i8>,
12765 ad: &CudaSlice<f32>,
12766 m: usize,
12767 ) -> Result<Option<((CudaSlice<f32>, f32), (CudaSlice<f32>, f32))>, Box<dyn std::error::Error>>
12768 {
12769 use crate::model::GpuTensor;
12770 if m != 1 || !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
12771 return Ok(None);
12772 }
12773 if !self.mmvq_supports(QT_NVFP4) {
12783 return Ok(None);
12784 }
12785 let (in_f, out_f) = (w0.in_features(), w0.out_features());
12786 if w1.in_features() != in_f || w1.out_features() != out_f {
12787 return Ok(None);
12788 }
12789 let no_mirror =
12802 |w: &crate::model::GpuTensor| !matches!(w, GpuTensor::Quant { rp4: Some(_), .. });
12803 if self.q8_ffn_fuse2_on()
12804 && no_mirror(w0)
12805 && no_mirror(w1)
12806 && let Some([p0, p1]) = self.q8_fused_params(&[w0, w1])
12807 {
12808 let (y0, y1) = self.q8_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2)?;
12809 return Ok(Some(((y0, 1.0), (y1, 1.0))));
12810 }
12811 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
12821 let (y0, y1) =
12822 self.e4m3_fused2_core(p0.0, p1.0, aq, ad, in_f, p0.1, p1.1, p0.2, 1.0, 1.0)?;
12823 return Ok(Some(((y0, p0.3), (y1, p1.3))));
12824 }
12825 let (b0, q0, rb0, s0, rp0) = match w0 {
12826 GpuTensor::Quant {
12827 bytes,
12828 qtype,
12829 row_bytes,
12830 scale,
12831 rp,
12832 ..
12833 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12834 _ => return Ok(None),
12835 };
12836 let (b1, q1, rb1, s1, rp1) = match w1 {
12837 GpuTensor::Quant {
12838 bytes,
12839 qtype,
12840 row_bytes,
12841 scale,
12842 rp,
12843 ..
12844 } => (bytes, *qtype, *row_bytes, *scale, *rp),
12845 _ => return Ok(None),
12846 };
12847 if q0 != QT_NVFP4 || q1 != QT_NVFP4 || rb0 != rb1 || rp0 != rp1 {
12848 return Ok(None);
12849 }
12850 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
12852 let rows_per_block = ROWS_PER_BLOCK * RPW;
12853 let f = self.func(if rp0 {
12854 "qmatvec_nvfp4_mmvq_dual_mr2_rp"
12855 } else {
12856 "qmatvec_nvfp4_mmvq_dual_mr2"
12857 });
12858 let mut y0 = self.alloc_uninit::<f32>(out_f)?;
12859 let mut y1 = self.alloc_uninit::<f32>(out_f)?;
12860 let cfg = LaunchConfig {
12861 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 2, 1),
12862 block_dim: (32, ROWS_PER_BLOCK, 1),
12863 shared_mem_bytes: 0,
12864 };
12865 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, rb0 as i64);
12866 let one = 1.0f32;
12869 let __s_b = self.gpu.stream();
12870 let mut b = __s_b.launch_builder(&f);
12871 b.arg(b0)
12872 .arg(b1)
12873 .arg(aq)
12874 .arg(ad)
12875 .arg(&mut y0)
12876 .arg(&mut y1)
12877 .arg(&inf)
12878 .arg(&outf)
12879 .arg(&mi)
12880 .arg(&rb)
12881 .arg(&one)
12882 .arg(&one);
12883 unsafe {
12884 b.launch(cfg)?;
12885 }
12886 Ok(Some(((y0, s0), (y1, s1))))
12887 }
12888
12889 #[allow(clippy::too_many_arguments)]
12897 pub fn matmul_nvfp4_fused3(
12898 &self,
12899 w0: &crate::model::GpuTensor,
12900 w1: &crate::model::GpuTensor,
12901 w2: &crate::model::GpuTensor,
12902 aq: &CudaSlice<i8>,
12903 ad: &CudaSlice<f32>,
12904 m: usize,
12905 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
12906 {
12907 use crate::model::GpuTensor;
12908 if !self.mmvq_supports(QT_NVFP4)
12915 || !self.uses_q8_1_fast(w0)
12916 || !self.uses_q8_1_fast(w1)
12917 || !self.uses_q8_1_fast(w2)
12918 {
12919 return Ok(None);
12920 }
12921 if (9..=16).contains(&m) {
12924 return Ok(
12925 match self.matmul_decode_exact_group3_pre([w0, w1, w2], aq, ad, m)? {
12926 Some(mut ys) => {
12927 let y2 = ys.pop().unwrap();
12928 let y1 = ys.pop().unwrap();
12929 let y0 = ys.pop().unwrap();
12930 Some((y0, y1, y2))
12931 }
12932 None => None,
12933 },
12934 );
12935 }
12936 if !(1..=8).contains(&m) {
12937 return Ok(None);
12938 }
12939 if m > 1 {
12940 let in_f = w0.in_features();
12941 if std::env::var("MEMRA_NVFP4_FUSED3B").as_deref() == Ok("0")
12942 || !self.batched_supports(QT_NVFP4)
12943 || std::env::var("MEMRA_NO_BATCHED").is_ok()
12944 || (m > 4 && !Self::b8_enabled())
12945 || in_f % 512 != 0
12946 || in_f / 64 > 272
12947 {
12948 return Ok(None);
12949 }
12950 }
12951 let unpack = |w: &crate::model::GpuTensor| match w {
12952 GpuTensor::Quant {
12953 bytes,
12954 qtype,
12955 scale,
12956 rp,
12957 ..
12958 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
12959 _ => None,
12960 };
12961 let (Some(p0), Some(p1), Some(p2)) = (unpack(w0), unpack(w1), unpack(w2)) else {
12962 return Ok(None);
12963 };
12964 let in_f = w0.in_features();
12965 if w1.in_features() != in_f || w2.in_features() != in_f {
12966 return Ok(None);
12967 }
12968 let (o0, o1, o2) = (w0.out_features(), w1.out_features(), w2.out_features());
12969 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
12971 let rows_pb = ROWS_PER_BLOCK * RPW;
12972 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
12973 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
12974 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
12975 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
12976 let (inf, oi0, oi1, oi2, mi) = (in_f as i32, o0 as i32, o1 as i32, o2 as i32, m as i32);
12977 let (b0, b1, b2) = unsafe { (&*p0.0, &*p1.0, &*p2.0) };
12980 if m > 1 {
12981 if p0.1 != 1.0 || p1.1 != 1.0 || p2.1 != 1.0 {
12983 return Ok(None);
12984 }
12985 let f = self.func("qmatvec_nvfp4_mmvq_fused3_b8_rpsc");
12986 let cfg = LaunchConfig {
12987 grid_dim: (nb(o0) + nb(o1) + nb(o2), 1, 1),
12988 block_dim: (32, ROWS_PER_BLOCK, 1),
12989 shared_mem_bytes: 0,
12990 };
12991 let __s_b = self.gpu.stream();
12992 let mut b = __s_b.launch_builder(&f);
12993 b.arg(b0)
12994 .arg(b1)
12995 .arg(b2)
12996 .arg(aq)
12997 .arg(ad)
12998 .arg(&mut y0)
12999 .arg(&mut y1)
13000 .arg(&mut y2)
13001 .arg(&inf)
13002 .arg(&oi0)
13003 .arg(&oi1)
13004 .arg(&oi2)
13005 .arg(&mi);
13006 unsafe {
13007 b.launch(cfg)?;
13008 }
13009 return Ok(Some((y0, y1, y2)));
13010 }
13011 let f = self.func("qmatvec_nvfp4_mmvq_fused3_rp");
13012 let cfg = LaunchConfig {
13013 grid_dim: (nb(o0) + nb(o1) + nb(o2), m as u32, 1),
13014 block_dim: (32, ROWS_PER_BLOCK, 1),
13015 shared_mem_bytes: 0,
13016 };
13017 let __s_b = self.gpu.stream();
13018 let mut b = __s_b.launch_builder(&f);
13019 b.arg(b0)
13020 .arg(b1)
13021 .arg(b2)
13022 .arg(aq)
13023 .arg(ad)
13024 .arg(&mut y0)
13025 .arg(&mut y1)
13026 .arg(&mut y2)
13027 .arg(&inf)
13028 .arg(&oi0)
13029 .arg(&oi1)
13030 .arg(&oi2)
13031 .arg(&mi)
13032 .arg(&p0.1)
13033 .arg(&p1.1)
13034 .arg(&p2.1);
13035 unsafe {
13036 b.launch(cfg)?;
13037 }
13038 Ok(Some((y0, y1, y2)))
13039 }
13040
13041 pub fn matmul_nvfp4_fused2(
13050 &self,
13051 w0: &crate::model::GpuTensor,
13052 w1: &crate::model::GpuTensor,
13053 aq: &CudaSlice<i8>,
13054 ad: &CudaSlice<f32>,
13055 m: usize,
13056 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
13057 use crate::model::GpuTensor;
13058 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13059 let off =
13060 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
13061 if off
13064 || m != 1
13065 || !self.mmvq_supports(QT_NVFP4)
13066 || !self.uses_q8_1_fast(w0)
13067 || !self.uses_q8_1_fast(w1)
13068 {
13069 return Ok(None);
13070 }
13071 let unpack = |w: &crate::model::GpuTensor| match w {
13072 GpuTensor::Quant {
13073 bytes,
13074 qtype,
13075 scale,
13076 rp,
13077 ..
13078 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
13079 _ => None,
13080 };
13081 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
13082 return Ok(None);
13083 };
13084 let in_f = w0.in_features();
13085 if w1.in_features() != in_f {
13086 return Ok(None);
13087 }
13088 let (o0, o1) = (w0.out_features(), w1.out_features());
13089 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
13091 let rows_pb = ROWS_PER_BLOCK * RPW;
13092 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
13093 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
13094 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
13095 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
13096 let cfg = LaunchConfig {
13097 grid_dim: (nb(o0) + nb(o1), m as u32, 1),
13098 block_dim: (32, ROWS_PER_BLOCK, 1),
13099 shared_mem_bytes: 0,
13100 };
13101 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, m as i32);
13102 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
13105 if Self::pdl_on() && Self::pdl_mmvq_on() && Self::pdl_nvfp4q8_on() {
13108 {
13109 use cudarc::driver::{DevicePtr, DevicePtrMut};
13110 let s = &self.gpu.stream();
13111 let (pw0, _g0) = b0.device_ptr(s);
13112 let (pw1, _g1) = b1.device_ptr(s);
13113 let (paq, _g2) = aq.device_ptr(s);
13114 let (pad, _g3) = ad.device_ptr(s);
13115 let (py0, _g4) = y0.device_ptr_mut(s);
13116 let (py1, _g5) = y1.device_ptr_mut(s);
13117 let (s0, s1) = (p0.1, p1.1);
13118 let mut ps = [
13119 &pw0 as *const _ as *mut std::ffi::c_void,
13120 &pw1 as *const _ as *mut _,
13121 &paq as *const _ as *mut _,
13122 &pad as *const _ as *mut _,
13123 &py0 as *const _ as *mut _,
13124 &py1 as *const _ as *mut _,
13125 &inf as *const _ as *mut _,
13126 &oi0 as *const _ as *mut _,
13127 &oi1 as *const _ as *mut _,
13128 &mi as *const _ as *mut _,
13129 &s0 as *const _ as *mut _,
13130 &s1 as *const _ as *mut _,
13131 ];
13132 unsafe {
13133 self.launch_pdl(
13134 "qmatvec_nvfp4_mmvq_fused2_rp",
13135 cfg.grid_dim,
13136 cfg.block_dim,
13137 &mut ps,
13138 )?;
13139 }
13140 }
13141 return Ok(Some((y0, y1)));
13142 }
13143 let __s_b = self.gpu.stream();
13144 let mut b = __s_b.launch_builder(&f);
13145 b.arg(b0)
13146 .arg(b1)
13147 .arg(aq)
13148 .arg(ad)
13149 .arg(&mut y0)
13150 .arg(&mut y1)
13151 .arg(&inf)
13152 .arg(&oi0)
13153 .arg(&oi1)
13154 .arg(&mi)
13155 .arg(&p0.1)
13156 .arg(&p1.1);
13157 unsafe {
13158 b.launch(cfg)?;
13159 }
13160 Ok(Some((y0, y1)))
13161 }
13162
13163 pub fn matmul_nvfp4_fused2_into(
13168 &self,
13169 w0: &crate::model::GpuTensor,
13170 w1: &crate::model::GpuTensor,
13171 aq: &CudaSlice<i8>,
13172 ad: &CudaSlice<f32>,
13173 y0: &mut CudaSlice<f32>,
13174 y1: &mut CudaSlice<f32>,
13175 ) -> Result<bool, Box<dyn std::error::Error>> {
13176 use crate::model::GpuTensor;
13177 static FUSED2_OFF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
13178 let off =
13179 *FUSED2_OFF.get_or_init(|| std::env::var("MEMRA_NVFP4_FUSED2").as_deref() == Ok("0"));
13180 if off
13181 || !self.mmvq_supports(QT_NVFP4)
13182 || !self.uses_q8_1_fast(w0)
13183 || !self.uses_q8_1_fast(w1)
13184 {
13185 return Ok(false);
13186 }
13187 let unpack = |w: &crate::model::GpuTensor| match w {
13188 GpuTensor::Quant {
13189 bytes,
13190 qtype,
13191 scale,
13192 rp,
13193 ..
13194 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
13195 _ => None,
13196 };
13197 let (Some(p0), Some(p1)) = (unpack(w0), unpack(w1)) else {
13198 return Ok(false);
13199 };
13200 let in_f = w0.in_features();
13201 if w1.in_features() != in_f {
13202 return Ok(false);
13203 }
13204 let (o0, o1) = (w0.out_features(), w1.out_features());
13205 if y0.len() < o0 || y1.len() < o1 {
13206 return Ok(false);
13207 }
13208 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
13210 let rows_pb = ROWS_PER_BLOCK * RPW;
13211 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
13212 let f = self.func("qmatvec_nvfp4_mmvq_fused2_rp");
13213 let cfg = LaunchConfig {
13214 grid_dim: (nb(o0) + nb(o1), 1, 1),
13215 block_dim: (32, ROWS_PER_BLOCK, 1),
13216 shared_mem_bytes: 0,
13217 };
13218 let (inf, oi0, oi1, mi) = (in_f as i32, o0 as i32, o1 as i32, 1i32);
13219 let (b0, b1) = unsafe { (&*p0.0, &*p1.0) };
13222 let __s_b = self.gpu.stream();
13223 let mut b = __s_b.launch_builder(&f);
13224 b.arg(b0)
13225 .arg(b1)
13226 .arg(aq)
13227 .arg(ad)
13228 .arg(&mut *y0)
13229 .arg(&mut *y1)
13230 .arg(&inf)
13231 .arg(&oi0)
13232 .arg(&oi1)
13233 .arg(&mi)
13234 .arg(&p0.1)
13235 .arg(&p1.1);
13236 unsafe {
13237 b.launch(cfg)?;
13238 }
13239 Ok(true)
13240 }
13241
13242 #[allow(clippy::type_complexity)]
13247 pub fn matmul_nvfp4_fused4(
13248 &self,
13249 w0: &crate::model::GpuTensor,
13250 w1: &crate::model::GpuTensor,
13251 w2: &crate::model::GpuTensor,
13252 w3: &crate::model::GpuTensor,
13253 aq: &CudaSlice<i8>,
13254 ad: &CudaSlice<f32>,
13255 m: usize,
13256 ) -> Result<
13257 Option<(
13258 CudaSlice<f32>,
13259 CudaSlice<f32>,
13260 CudaSlice<f32>,
13261 CudaSlice<f32>,
13262 )>,
13263 Box<dyn std::error::Error>,
13264 > {
13265 use crate::model::GpuTensor;
13266 if std::env::var("MEMRA_NVFP4_FUSED4").as_deref() == Ok("0")
13273 || !self.mmvq_supports(QT_NVFP4)
13274 || !self.uses_q8_1_fast(w0)
13275 || !self.uses_q8_1_fast(w1)
13276 || !self.uses_q8_1_fast(w2)
13277 || !self.uses_q8_1_fast(w3)
13278 {
13279 return Ok(None);
13280 }
13281 if (9..=16).contains(&m) {
13286 return Ok(
13287 match self.matmul_decode_exact_group4_pre([w0, w1, w2, w3], aq, ad, m)? {
13288 Some(mut ys) => {
13289 let y3 = ys.pop().unwrap();
13290 let y2 = ys.pop().unwrap();
13291 let y1 = ys.pop().unwrap();
13292 let y0 = ys.pop().unwrap();
13293 Some((y0, y1, y2, y3))
13294 }
13295 None => None,
13296 },
13297 );
13298 }
13299 if !(1..=8).contains(&m) {
13300 return Ok(None);
13301 }
13302 if m > 1 {
13303 let in_f = w0.in_features();
13306 if !self.batched_supports(QT_NVFP4)
13307 || std::env::var("MEMRA_NO_BATCHED").is_ok()
13308 || (m > 4 && !Self::b8_enabled())
13309 || in_f % 512 != 0
13310 || in_f / 64 > 272
13311 {
13312 return Ok(None);
13313 }
13314 }
13315 let unpack = |w: &crate::model::GpuTensor| match w {
13316 GpuTensor::Quant {
13317 bytes,
13318 qtype,
13319 scale,
13320 rp,
13321 ..
13322 } if *qtype == QT_NVFP4 && *rp => Some((bytes as *const CudaSlice<u8>, *scale)),
13323 _ => None,
13324 };
13325 let (Some(p0), Some(p1), Some(p2), Some(p3)) =
13326 (unpack(w0), unpack(w1), unpack(w2), unpack(w3))
13327 else {
13328 return Ok(None);
13329 };
13330 let in_f = w0.in_features();
13331 if w1.in_features() != in_f || w2.in_features() != in_f || w3.in_features() != in_f {
13332 return Ok(None);
13333 }
13334 let (o0, o1, o2, o3) = (
13335 w0.out_features(),
13336 w1.out_features(),
13337 w2.out_features(),
13338 w3.out_features(),
13339 );
13340 const ROWS_PER_BLOCK: u32 = 4; const RPW: u32 = 2;
13342 let rows_pb = ROWS_PER_BLOCK * RPW;
13343 let nb = |o: usize| (o as u32).div_ceil(rows_pb);
13344 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
13345 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
13346 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
13347 let mut y3 = self.alloc_uninit::<f32>(m * o3)?;
13348 let (inf, oi0, oi1, oi2, oi3, mi) = (
13349 in_f as i32,
13350 o0 as i32,
13351 o1 as i32,
13352 o2 as i32,
13353 o3 as i32,
13354 m as i32,
13355 );
13356 let (b0, b1, b2, b3) = unsafe { (&*p0.0, &*p1.0, &*p2.0, &*p3.0) };
13359 if m > 1 {
13360 if p0.1 != 1.0 || p1.1 != 1.0 || p2.1 != 1.0 || p3.1 != 1.0 {
13363 return Ok(None);
13364 }
13365 let f = self.func("qmatvec_nvfp4_mmvq_fused4_b8_rpsc");
13366 let cfg = LaunchConfig {
13367 grid_dim: (nb(o0) + nb(o1) + nb(o2) + nb(o3), 1, 1),
13368 block_dim: (32, ROWS_PER_BLOCK, 1),
13369 shared_mem_bytes: 0,
13370 };
13371 let __s_b = self.gpu.stream();
13372 let mut b = __s_b.launch_builder(&f);
13373 b.arg(b0)
13374 .arg(b1)
13375 .arg(b2)
13376 .arg(b3)
13377 .arg(aq)
13378 .arg(ad)
13379 .arg(&mut y0)
13380 .arg(&mut y1)
13381 .arg(&mut y2)
13382 .arg(&mut y3)
13383 .arg(&inf)
13384 .arg(&oi0)
13385 .arg(&oi1)
13386 .arg(&oi2)
13387 .arg(&oi3)
13388 .arg(&mi);
13389 unsafe {
13390 b.launch(cfg)?;
13391 }
13392 return Ok(Some((y0, y1, y2, y3)));
13393 }
13394 let f = self.func("qmatvec_nvfp4_mmvq_fused4_rp");
13395 let cfg = LaunchConfig {
13396 grid_dim: (nb(o0) + nb(o1) + nb(o2) + nb(o3), m as u32, 1),
13397 block_dim: (32, ROWS_PER_BLOCK, 1),
13398 shared_mem_bytes: 0,
13399 };
13400 let __s_b = self.gpu.stream();
13401 let mut b = __s_b.launch_builder(&f);
13402 b.arg(b0)
13403 .arg(b1)
13404 .arg(b2)
13405 .arg(b3)
13406 .arg(aq)
13407 .arg(ad)
13408 .arg(&mut y0)
13409 .arg(&mut y1)
13410 .arg(&mut y2)
13411 .arg(&mut y3)
13412 .arg(&inf)
13413 .arg(&oi0)
13414 .arg(&oi1)
13415 .arg(&oi2)
13416 .arg(&oi3)
13417 .arg(&mi)
13418 .arg(&p0.1)
13419 .arg(&p1.1)
13420 .arg(&p2.1)
13421 .arg(&p3.1);
13422 unsafe {
13423 b.launch(cfg)?;
13424 }
13425 Ok(Some((y0, y1, y2, y3)))
13426 }
13427
13428 pub fn matmul_q8_fused2(
13436 &self,
13437 w0: &crate::model::GpuTensor,
13438 w1: &crate::model::GpuTensor,
13439 aq: &CudaSlice<i8>,
13440 ad: &CudaSlice<f32>,
13441 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
13442 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
13448 return Ok(Some(self.e4m3_fused2_core(
13449 p0.0,
13450 p1.0,
13451 aq,
13452 ad,
13453 w0.in_features(),
13454 p0.1,
13455 p1.1,
13456 p0.2,
13457 p0.3,
13458 p1.3,
13459 )?));
13460 }
13461 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
13462 return Ok(None);
13463 };
13464 Ok(Some(self.q8_fused2_core(
13465 p0.0,
13466 p1.0,
13467 aq,
13468 ad,
13469 w0.in_features(),
13470 p0.1,
13471 p1.1,
13472 p0.2,
13473 )?))
13474 }
13475
13476 #[allow(clippy::too_many_arguments)]
13477 fn q8_fused2_core(
13478 &self,
13479 b0: &CudaSlice<u8>,
13480 b1: &CudaSlice<u8>,
13481 aq: &CudaSlice<i8>,
13482 ad: &CudaSlice<f32>,
13483 in_f: usize,
13484 out0: usize,
13485 out1: usize,
13486 row_bytes: usize,
13487 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13488 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
13490 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
13491 let f = self.func("qmatvec_q8_0_mmvq_fused2");
13492 let mut y0 = self.alloc_uninit::<f32>(out0)?;
13493 let mut y1 = self.alloc_uninit::<f32>(out1)?;
13494 let cfg = LaunchConfig {
13495 grid_dim: (nb0 + nb1, 1, 1),
13496 block_dim: (32, ROWS_PER_BLOCK, 1),
13497 shared_mem_bytes: 0,
13498 };
13499 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
13500 let __s_b = self.gpu.stream();
13501 let mut b = __s_b.launch_builder(&f);
13502 b.arg(b0)
13503 .arg(b1)
13504 .arg(aq)
13505 .arg(ad)
13506 .arg(&mut y0)
13507 .arg(&mut y1)
13508 .arg(&inf)
13509 .arg(&o0)
13510 .arg(&o1)
13511 .arg(&rbl);
13512 unsafe {
13513 b.launch(cfg)?;
13514 }
13515 Ok((y0, y1))
13516 }
13517
13518 pub fn matmul_q8_fused2_x(
13524 &self,
13525 w0: &crate::model::GpuTensor,
13526 w1: &crate::model::GpuTensor,
13527 x: &CudaSlice<f32>,
13528 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
13529 if !self.uses_q8_1_fast(w0) || !self.uses_q8_1_fast(w1) {
13530 return Ok(None);
13531 }
13532 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
13533 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
13534 return Ok(Some(self.e4m3_fused2_core(
13535 p0.0,
13536 p1.0,
13537 &aq,
13538 &ad,
13539 w0.in_features(),
13540 p0.1,
13541 p1.1,
13542 p0.2,
13543 p0.3,
13544 p1.3,
13545 )?));
13546 }
13547 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
13548 return Ok(None);
13549 };
13550 let (aq, ad) = self.quantize_q8_1(x, 1, w0.in_features())?;
13551 Ok(Some(self.q8_fused2_core(
13552 p0.0,
13553 p1.0,
13554 &aq,
13555 &ad,
13556 w0.in_features(),
13557 p0.1,
13558 p1.1,
13559 p0.2,
13560 )?))
13561 }
13562
13563 #[allow(clippy::too_many_arguments)]
13566 pub fn qmatvec_q8_fused2_raw(
13567 &self,
13568 b0: &CudaSlice<u8>,
13569 b1: &CudaSlice<u8>,
13570 x: &CudaSlice<f32>,
13571 in_f: usize,
13572 out0: usize,
13573 out1: usize,
13574 row_bytes: usize,
13575 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
13576 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
13577 self.q8_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes)
13578 }
13579
13580 pub fn matmul_q4_fused3(
13586 &self,
13587 w0: &crate::model::GpuTensor,
13588 w1: &crate::model::GpuTensor,
13589 w2: &crate::model::GpuTensor,
13590 aq: &CudaSlice<i8>,
13591 ad: &CudaSlice<f32>,
13592 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
13593 {
13594 use crate::model::GpuTensor;
13595 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
13596 match w {
13597 GpuTensor::Quant {
13598 qtype, row_bytes, ..
13599 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
13600 _ => None,
13601 }
13602 };
13603 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
13604 return Ok(None);
13605 };
13606 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
13607 return Ok(None);
13608 }
13609 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
13613 match w {
13614 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
13615 Some(m) => (m, true),
13616 None => (bytes, *rp),
13617 },
13618 _ => unreachable!(),
13619 }
13620 }
13621 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
13622 if rp0 != rp1 || rp1 != rp2 {
13623 return Ok(None);
13624 }
13625 let rp = rp0;
13626 let rpb: u32 = 4;
13627 let mr1 = rp && Self::q40_mr1_on();
13631 let nb = |o: usize| {
13632 if mr1 {
13633 (o as u32).div_ceil(rpb)
13634 } else {
13635 (o as u32).div_ceil(2).div_ceil(rpb)
13636 }
13637 };
13638 let grid = nb(o0) + nb(o1) + nb(o2);
13639 let mut y0 = self.alloc_uninit::<f32>(o0)?;
13640 let mut y1 = self.alloc_uninit::<f32>(o1)?;
13641 let mut y2 = self.alloc_uninit::<f32>(o2)?;
13642 let f = self.func(if mr1 {
13643 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
13644 } else if rp {
13645 "qmatvec_q4_0_mmvq_fused3_rp"
13646 } else {
13647 "qmatvec_q4_0_mmvq_fused3"
13648 });
13649 let cfg = LaunchConfig {
13650 grid_dim: (grid, 1, 1),
13651 block_dim: (32, rpb, 1),
13652 shared_mem_bytes: 0,
13653 };
13654 let inf = w0.in_features() as i32;
13655 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
13656 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
13657 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
13660 {
13661 use cudarc::driver::{DevicePtr, DevicePtrMut};
13662 let s = &self.gpu.stream();
13663 let (p0, _g0) = b0.device_ptr(s);
13664 let (p1, _g1) = b1.device_ptr(s);
13665 let (p2, _g2) = b2.device_ptr(s);
13666 let (paq, _g3) = aq.device_ptr(s);
13667 let (pad, _g4) = ad.device_ptr(s);
13668 let (py0, _g5) = y0.device_ptr_mut(s);
13669 let (py1, _g6) = y1.device_ptr_mut(s);
13670 let (py2, _g7) = y2.device_ptr_mut(s);
13671 let mut ps = [
13672 &p0 as *const _ as *mut std::ffi::c_void,
13673 &p1 as *const _ as *mut _,
13674 &p2 as *const _ as *mut _,
13675 &paq as *const _ as *mut _,
13676 &pad as *const _ as *mut _,
13677 &py0 as *const _ as *mut _,
13678 &py1 as *const _ as *mut _,
13679 &py2 as *const _ as *mut _,
13680 &inf as *const _ as *mut _,
13681 &oo0 as *const _ as *mut _,
13682 &oo1 as *const _ as *mut _,
13683 &oo2 as *const _ as *mut _,
13684 &r0 as *const _ as *mut _,
13685 &r1 as *const _ as *mut _,
13686 &r2 as *const _ as *mut _,
13687 ];
13688 unsafe {
13689 self.launch_pdl(
13690 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
13691 (grid, 1, 1),
13692 (32, rpb, 1),
13693 &mut ps,
13694 )?;
13695 }
13696 }
13697 return Ok(Some((y0, y1, y2)));
13698 }
13699 let __s_b = self.gpu.stream();
13700 let mut b = __s_b.launch_builder(&f);
13701 b.arg(b0)
13702 .arg(b1)
13703 .arg(b2)
13704 .arg(aq)
13705 .arg(ad)
13706 .arg(&mut y0)
13707 .arg(&mut y1)
13708 .arg(&mut y2)
13709 .arg(&inf)
13710 .arg(&oo0)
13711 .arg(&oo1)
13712 .arg(&oo2)
13713 .arg(&r0)
13714 .arg(&r1)
13715 .arg(&r2);
13716 unsafe {
13717 b.launch(cfg)?;
13718 }
13719 Ok(Some((y0, y1, y2)))
13720 }
13721
13722 #[allow(clippy::too_many_arguments)]
13725 pub fn matmul_q4_fused3_into(
13726 &self,
13727 w0: &crate::model::GpuTensor,
13728 w1: &crate::model::GpuTensor,
13729 w2: &crate::model::GpuTensor,
13730 aq: &CudaSlice<i8>,
13731 ad: &CudaSlice<f32>,
13732 y0: &mut CudaSlice<f32>,
13733 y1: &mut CudaSlice<f32>,
13734 y2: &mut CudaSlice<f32>,
13735 ) -> Result<bool, Box<dyn std::error::Error>> {
13736 use crate::model::GpuTensor;
13737 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
13738 match w {
13739 GpuTensor::Quant {
13740 qtype, row_bytes, ..
13741 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
13742 _ => None,
13743 }
13744 };
13745 let (Some((rb0, o0)), Some((rb1, o1)), Some((rb2, o2))) = (q4(w0), q4(w1), q4(w2)) else {
13746 return Ok(false);
13747 };
13748 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
13749 return Ok(false);
13750 }
13751 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
13752 match w {
13753 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
13754 Some(m) => (m, true),
13755 None => (bytes, *rp),
13756 },
13757 _ => unreachable!(),
13758 }
13759 }
13760 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
13761 if rp0 != rp1 || rp1 != rp2 {
13762 return Ok(false);
13763 }
13764 let rp = rp0;
13765 let rpb: u32 = 4;
13766 let mr1 = rp && Self::q40_mr1_on();
13767 let nb = |o: usize| {
13768 if mr1 {
13769 (o as u32).div_ceil(rpb)
13770 } else {
13771 (o as u32).div_ceil(2).div_ceil(rpb)
13772 }
13773 };
13774 let grid = nb(o0) + nb(o1) + nb(o2);
13775 debug_assert!(y0.len() >= o0 && y1.len() >= o1 && y2.len() >= o2);
13776 let f = self.func(if mr1 {
13777 "qmatvec_q4_0_mmvq_fused3_mr1_rp"
13778 } else if rp {
13779 "qmatvec_q4_0_mmvq_fused3_rp"
13780 } else {
13781 "qmatvec_q4_0_mmvq_fused3"
13782 });
13783 let cfg = LaunchConfig {
13784 grid_dim: (grid, 1, 1),
13785 block_dim: (32, rpb, 1),
13786 shared_mem_bytes: 0,
13787 };
13788 let inf = w0.in_features() as i32;
13789 let (oo0, oo1, oo2) = (o0 as i32, o1 as i32, o2 as i32);
13790 let (r0, r1, r2) = (rb0 as i64, rb1 as i64, rb2 as i64);
13791 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
13793 use cudarc::driver::{DevicePtr, DevicePtrMut};
13794 let s = &self.gpu.stream();
13795 let (p0, _g0) = b0.device_ptr(s);
13796 let (p1, _g1) = b1.device_ptr(s);
13797 let (p2, _g2) = b2.device_ptr(s);
13798 let (paq, _g3) = aq.device_ptr(s);
13799 let (pad, _g4) = ad.device_ptr(s);
13800 let (py0, _g5) = y0.device_ptr_mut(s);
13801 let (py1, _g6) = y1.device_ptr_mut(s);
13802 let (py2, _g7) = y2.device_ptr_mut(s);
13803 let mut ps = [
13804 &p0 as *const _ as *mut std::ffi::c_void,
13805 &p1 as *const _ as *mut _,
13806 &p2 as *const _ as *mut _,
13807 &paq as *const _ as *mut _,
13808 &pad as *const _ as *mut _,
13809 &py0 as *const _ as *mut _,
13810 &py1 as *const _ as *mut _,
13811 &py2 as *const _ as *mut _,
13812 &inf as *const _ as *mut _,
13813 &oo0 as *const _ as *mut _,
13814 &oo1 as *const _ as *mut _,
13815 &oo2 as *const _ as *mut _,
13816 &r0 as *const _ as *mut _,
13817 &r1 as *const _ as *mut _,
13818 &r2 as *const _ as *mut _,
13819 ];
13820 unsafe {
13821 self.launch_pdl(
13822 "qmatvec_q4_0_mmvq_fused3_mr1_rp",
13823 (grid, 1, 1),
13824 (32, rpb, 1),
13825 &mut ps,
13826 )?;
13827 }
13828 return Ok(true);
13829 }
13830 let __s_b = self.gpu.stream();
13831 let mut b = __s_b.launch_builder(&f);
13832 b.arg(b0)
13833 .arg(b1)
13834 .arg(b2)
13835 .arg(aq)
13836 .arg(ad)
13837 .arg(&mut *y0)
13838 .arg(&mut *y1)
13839 .arg(&mut *y2)
13840 .arg(&inf)
13841 .arg(&oo0)
13842 .arg(&oo1)
13843 .arg(&oo2)
13844 .arg(&r0)
13845 .arg(&r1)
13846 .arg(&r2);
13847 unsafe {
13848 b.launch(cfg)?;
13849 }
13850 Ok(true)
13851 }
13852
13853 pub fn matmul_q4_fused2(
13855 &self,
13856 w0: &crate::model::GpuTensor,
13857 w1: &crate::model::GpuTensor,
13858 aq: &CudaSlice<i8>,
13859 ad: &CudaSlice<f32>,
13860 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
13861 use crate::model::GpuTensor;
13862 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
13863 match w {
13864 GpuTensor::Quant {
13865 qtype, row_bytes, ..
13866 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
13867 _ => None,
13868 }
13869 };
13870 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
13871 return Ok(None);
13872 };
13873 if w0.in_features() != w1.in_features() {
13874 return Ok(None);
13875 }
13876 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
13878 match w {
13879 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
13880 Some(m) => (m, true),
13881 None => (bytes, *rp),
13882 },
13883 _ => unreachable!(),
13884 }
13885 }
13886 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
13887 if rp0 != rp1 {
13888 return Ok(None);
13889 }
13890 let rp = rp0;
13891 let rpb: u32 = 4;
13892 let mr1 = rp && Self::q40_mr1_on();
13894 let nb = |o: usize| {
13895 if mr1 {
13896 (o as u32).div_ceil(rpb)
13897 } else {
13898 (o as u32).div_ceil(2).div_ceil(rpb)
13899 }
13900 };
13901 let grid = nb(o0) + nb(o1);
13902 let mut y0 = self.alloc_uninit::<f32>(o0)?;
13903 let mut y1 = self.alloc_uninit::<f32>(o1)?;
13904 let f = self.func(if mr1 {
13905 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
13906 } else if rp {
13907 "qmatvec_q4_0_mmvq_fused2_rp"
13908 } else {
13909 "qmatvec_q4_0_mmvq_fused2"
13910 });
13911 let cfg = LaunchConfig {
13912 grid_dim: (grid, 1, 1),
13913 block_dim: (32, rpb, 1),
13914 shared_mem_bytes: 0,
13915 };
13916 let inf = w0.in_features() as i32;
13917 let (oo0, oo1) = (o0 as i32, o1 as i32);
13918 let (r0, r1) = (rb0 as i64, rb1 as i64);
13919 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
13921 {
13922 use cudarc::driver::{DevicePtr, DevicePtrMut};
13923 let s = &self.gpu.stream();
13924 let (p0, _g0) = b0.device_ptr(s);
13925 let (p1, _g1) = b1.device_ptr(s);
13926 let (paq, _g2) = aq.device_ptr(s);
13927 let (pad, _g3) = ad.device_ptr(s);
13928 let (py0, _g4) = y0.device_ptr_mut(s);
13929 let (py1, _g5) = y1.device_ptr_mut(s);
13930 let mut ps = [
13931 &p0 as *const _ as *mut std::ffi::c_void,
13932 &p1 as *const _ as *mut _,
13933 &paq as *const _ as *mut _,
13934 &pad as *const _ as *mut _,
13935 &py0 as *const _ as *mut _,
13936 &py1 as *const _ as *mut _,
13937 &inf as *const _ as *mut _,
13938 &oo0 as *const _ as *mut _,
13939 &oo1 as *const _ as *mut _,
13940 &r0 as *const _ as *mut _,
13941 &r1 as *const _ as *mut _,
13942 ];
13943 unsafe {
13944 self.launch_pdl(
13945 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
13946 (grid, 1, 1),
13947 (32, rpb, 1),
13948 &mut ps,
13949 )?;
13950 }
13951 }
13952 return Ok(Some((y0, y1)));
13953 }
13954 let __s_b = self.gpu.stream();
13955 let mut b = __s_b.launch_builder(&f);
13956 b.arg(b0)
13957 .arg(b1)
13958 .arg(aq)
13959 .arg(ad)
13960 .arg(&mut y0)
13961 .arg(&mut y1)
13962 .arg(&inf)
13963 .arg(&oo0)
13964 .arg(&oo1)
13965 .arg(&r0)
13966 .arg(&r1);
13967 unsafe {
13968 b.launch(cfg)?;
13969 }
13970 Ok(Some((y0, y1)))
13971 }
13972
13973 pub fn matmul_q4_fused2_into(
13975 &self,
13976 w0: &crate::model::GpuTensor,
13977 w1: &crate::model::GpuTensor,
13978 aq: &CudaSlice<i8>,
13979 ad: &CudaSlice<f32>,
13980 y0: &mut CudaSlice<f32>,
13981 y1: &mut CudaSlice<f32>,
13982 ) -> Result<bool, Box<dyn std::error::Error>> {
13983 use crate::model::GpuTensor;
13984 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
13985 match w {
13986 GpuTensor::Quant {
13987 qtype, row_bytes, ..
13988 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
13989 _ => None,
13990 }
13991 };
13992 let (Some((rb0, o0)), Some((rb1, o1))) = (q4(w0), q4(w1)) else {
13993 return Ok(false);
13994 };
13995 if w0.in_features() != w1.in_features() {
13996 return Ok(false);
13997 }
13998 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
13999 match w {
14000 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
14001 Some(m) => (m, true),
14002 None => (bytes, *rp),
14003 },
14004 _ => unreachable!(),
14005 }
14006 }
14007 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
14008 if rp0 != rp1 {
14009 return Ok(false);
14010 }
14011 let rp = rp0;
14012 let rpb: u32 = 4;
14013 let mr1 = rp && Self::q40_mr1_on();
14014 let nb = |o: usize| {
14015 if mr1 {
14016 (o as u32).div_ceil(rpb)
14017 } else {
14018 (o as u32).div_ceil(2).div_ceil(rpb)
14019 }
14020 };
14021 let grid = nb(o0) + nb(o1);
14022 debug_assert!(y0.len() >= o0 && y1.len() >= o1);
14023 let f = self.func(if mr1 {
14024 "qmatvec_q4_0_mmvq_fused2_mr1_rp"
14025 } else if rp {
14026 "qmatvec_q4_0_mmvq_fused2_rp"
14027 } else {
14028 "qmatvec_q4_0_mmvq_fused2"
14029 });
14030 let cfg = LaunchConfig {
14031 grid_dim: (grid, 1, 1),
14032 block_dim: (32, rpb, 1),
14033 shared_mem_bytes: 0,
14034 };
14035 let inf = w0.in_features() as i32;
14036 let (oo0, oo1) = (o0 as i32, o1 as i32);
14037 let (r0, r1) = (rb0 as i64, rb1 as i64);
14038 if mr1 && Self::pdl_on() && Self::pdl_mmvq_on() {
14040 use cudarc::driver::{DevicePtr, DevicePtrMut};
14041 let s = &self.gpu.stream();
14042 let (p0, _g0) = b0.device_ptr(s);
14043 let (p1, _g1) = b1.device_ptr(s);
14044 let (paq, _g2) = aq.device_ptr(s);
14045 let (pad, _g3) = ad.device_ptr(s);
14046 let (py0, _g4) = y0.device_ptr_mut(s);
14047 let (py1, _g5) = y1.device_ptr_mut(s);
14048 let mut ps = [
14049 &p0 as *const _ as *mut std::ffi::c_void,
14050 &p1 as *const _ as *mut _,
14051 &paq as *const _ as *mut _,
14052 &pad as *const _ as *mut _,
14053 &py0 as *const _ as *mut _,
14054 &py1 as *const _ as *mut _,
14055 &inf as *const _ as *mut _,
14056 &oo0 as *const _ as *mut _,
14057 &oo1 as *const _ as *mut _,
14058 &r0 as *const _ as *mut _,
14059 &r1 as *const _ as *mut _,
14060 ];
14061 unsafe {
14062 self.launch_pdl(
14063 "qmatvec_q4_0_mmvq_fused2_mr1_rp",
14064 (grid, 1, 1),
14065 (32, rpb, 1),
14066 &mut ps,
14067 )?;
14068 }
14069 return Ok(true);
14070 }
14071 let __s_b = self.gpu.stream();
14072 let mut b = __s_b.launch_builder(&f);
14073 b.arg(b0)
14074 .arg(b1)
14075 .arg(aq)
14076 .arg(ad)
14077 .arg(&mut *y0)
14078 .arg(&mut *y1)
14079 .arg(&inf)
14080 .arg(&oo0)
14081 .arg(&oo1)
14082 .arg(&r0)
14083 .arg(&r1);
14084 unsafe {
14085 b.launch(cfg)?;
14086 }
14087 Ok(true)
14088 }
14089
14090 pub fn matmul_q4_fused2_batched(
14095 &self,
14096 w0: &crate::model::GpuTensor,
14097 w1: &crate::model::GpuTensor,
14098 aq: &CudaSlice<i8>,
14099 ad: &CudaSlice<f32>,
14100 m: usize,
14101 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
14102 use crate::model::GpuTensor;
14103 if m < 2 || m > 8 {
14104 return Ok(None);
14105 }
14106 let q4 = |w: &GpuTensor| -> Option<(usize, usize)> {
14107 match w {
14108 GpuTensor::Quant {
14109 qtype, row_bytes, ..
14110 } if *qtype == QT_Q4_0 => Some((*row_bytes, w.out_features())),
14111 _ => None,
14112 }
14113 };
14114 let (Some((rb0, o0)), Some((_rb1, o1))) = (q4(w0), q4(w1)) else {
14115 return Ok(None);
14116 };
14117 if w0.in_features() != w1.in_features() {
14118 return Ok(None);
14119 }
14120 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
14121 match w {
14122 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
14123 Some(mr) => (mr, true),
14124 None => (bytes, *rp),
14125 },
14126 _ => unreachable!(),
14127 }
14128 }
14129 let ((b0, rp0), (b1, rp1)) = (eff(w0), eff(w1));
14130 if !rp0 || !rp1 {
14131 return Ok(None);
14132 }
14133 let mcols = Self::batched_mcols(m);
14134 let rpb: u32 = 4;
14135 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
14136 let grid = nb(o0) + nb(o1);
14137 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
14138 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
14139 let f = self.func(match mcols {
14140 2 => "qmatvec_q4_0_mmvq_b2_f2_rp",
14141 4 => "qmatvec_q4_0_mmvq_b4_f2_rp",
14142 _ => "qmatvec_q4_0_mmvq_b8_f2_rp",
14143 });
14144 let cfg = LaunchConfig {
14145 grid_dim: (grid, 1, 1),
14146 block_dim: (32, rpb, 1),
14147 shared_mem_bytes: 0,
14148 };
14149 let inf = w0.in_features() as i32;
14150 let (oo0, oo1, mi) = (o0 as i32, o1 as i32, m as i32);
14151 let rb = rb0 as i64;
14152 let __s_b = self.gpu.stream();
14153 let mut b = __s_b.launch_builder(&f);
14154 b.arg(b0)
14155 .arg(b1)
14156 .arg(aq)
14157 .arg(ad)
14158 .arg(&mut y0)
14159 .arg(&mut y1)
14160 .arg(&inf)
14161 .arg(&oo0)
14162 .arg(&oo1)
14163 .arg(&mi)
14164 .arg(&rb);
14165 unsafe {
14166 b.launch(cfg)?;
14167 }
14168 Ok(Some((y0, y1)))
14169 }
14170
14171 #[allow(clippy::too_many_arguments)]
14174 pub fn matmul_q4_fused3_batched(
14175 &self,
14176 w0: &crate::model::GpuTensor,
14177 w1: &crate::model::GpuTensor,
14178 w2: &crate::model::GpuTensor,
14179 aq: &CudaSlice<i8>,
14180 ad: &CudaSlice<f32>,
14181 m: usize,
14182 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
14183 {
14184 use crate::model::GpuTensor;
14185 if m < 2 || m > 8 {
14186 return Ok(None);
14187 }
14188 let q4 = |w: &GpuTensor| -> Option<usize> {
14189 match w {
14190 GpuTensor::Quant { qtype, .. } if *qtype == QT_Q4_0 => Some(w.out_features()),
14191 _ => None,
14192 }
14193 };
14194 let (Some(o0), Some(o1), Some(o2)) = (q4(w0), q4(w1), q4(w2)) else {
14195 return Ok(None);
14196 };
14197 if w0.in_features() != w1.in_features() || w0.in_features() != w2.in_features() {
14198 return Ok(None);
14199 }
14200 fn eff(w: &GpuTensor) -> (&CudaSlice<u8>, bool) {
14201 match w {
14202 GpuTensor::Quant { bytes, rp4, rp, .. } => match rp4 {
14203 Some(mr) => (mr, true),
14204 None => (bytes, *rp),
14205 },
14206 _ => unreachable!(),
14207 }
14208 }
14209 let ((b0, rp0), (b1, rp1), (b2, rp2)) = (eff(w0), eff(w1), eff(w2));
14210 if !rp0 || !rp1 || !rp2 {
14211 return Ok(None);
14212 }
14213 let mcols = Self::batched_mcols(m);
14214 let rpb: u32 = 4;
14215 let nb = |o: usize| (o as u32).div_ceil(2 * rpb);
14216 let grid = nb(o0) + nb(o1) + nb(o2);
14217 let mut y0 = self.alloc_uninit::<f32>(m * o0)?;
14218 let mut y1 = self.alloc_uninit::<f32>(m * o1)?;
14219 let mut y2 = self.alloc_uninit::<f32>(m * o2)?;
14220 let f = self.func(match mcols {
14221 2 => "qmatvec_q4_0_mmvq_b2_f3_rp",
14222 4 => "qmatvec_q4_0_mmvq_b4_f3_rp",
14223 _ => "qmatvec_q4_0_mmvq_b8_f3_rp",
14224 });
14225 let cfg = LaunchConfig {
14226 grid_dim: (grid, 1, 1),
14227 block_dim: (32, rpb, 1),
14228 shared_mem_bytes: 0,
14229 };
14230 let inf = w0.in_features() as i32;
14231 let (oo0, oo1, oo2, mi) = (o0 as i32, o1 as i32, o2 as i32, m as i32);
14232 let rb = 0i64;
14233 let __s_b = self.gpu.stream();
14234 let mut b = __s_b.launch_builder(&f);
14235 b.arg(b0)
14236 .arg(b1)
14237 .arg(b2)
14238 .arg(aq)
14239 .arg(ad)
14240 .arg(&mut y0)
14241 .arg(&mut y1)
14242 .arg(&mut y2)
14243 .arg(&inf)
14244 .arg(&oo0)
14245 .arg(&oo1)
14246 .arg(&oo2)
14247 .arg(&mi)
14248 .arg(&rb);
14249 unsafe {
14250 b.launch(cfg)?;
14251 }
14252 Ok(Some((y0, y1, y2)))
14253 }
14254
14255 pub fn matmul_q8_fused3(
14256 &self,
14257 w0: &crate::model::GpuTensor,
14258 w1: &crate::model::GpuTensor,
14259 w2: &crate::model::GpuTensor,
14260 aq: &CudaSlice<i8>,
14261 ad: &CudaSlice<f32>,
14262 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
14263 {
14264 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
14267 return Ok(Some(self.e4m3_fused3_core(
14268 p0.0,
14269 p1.0,
14270 p2.0,
14271 aq,
14272 ad,
14273 w0.in_features(),
14274 p0.1,
14275 p1.1,
14276 p2.1,
14277 p0.2,
14278 p0.3,
14279 p1.3,
14280 p2.3,
14281 )?));
14282 }
14283 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
14284 return Ok(None);
14285 };
14286 Ok(Some(self.q8_fused3_core(
14287 p0.0,
14288 p1.0,
14289 p2.0,
14290 aq,
14291 ad,
14292 w0.in_features(),
14293 p0.1,
14294 p1.1,
14295 p2.1,
14296 p0.2,
14297 )?))
14298 }
14299
14300 #[allow(clippy::too_many_arguments)]
14301 fn q8_fused3_core(
14302 &self,
14303 b0: &CudaSlice<u8>,
14304 b1: &CudaSlice<u8>,
14305 b2: &CudaSlice<u8>,
14306 aq: &CudaSlice<i8>,
14307 ad: &CudaSlice<f32>,
14308 in_f: usize,
14309 out0: usize,
14310 out1: usize,
14311 out2: usize,
14312 row_bytes: usize,
14313 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14314 const ROWS_PER_BLOCK: u32 = 4;
14315 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
14316 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
14317 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
14318 let f = self.func("qmatvec_q8_0_mmvq_fused3");
14319 let mut y0 = self.alloc_uninit::<f32>(out0)?;
14320 let mut y1 = self.alloc_uninit::<f32>(out1)?;
14321 let mut y2 = self.alloc_uninit::<f32>(out2)?;
14322 let cfg = LaunchConfig {
14323 grid_dim: (nb0 + nb1 + nb2, 1, 1),
14324 block_dim: (32, ROWS_PER_BLOCK, 1),
14325 shared_mem_bytes: 0,
14326 };
14327 let (inf, o0, o1, o2, rbl) = (
14328 in_f as i32,
14329 out0 as i32,
14330 out1 as i32,
14331 out2 as i32,
14332 row_bytes as i64,
14333 );
14334 let __s_b = self.gpu.stream();
14335 let mut b = __s_b.launch_builder(&f);
14336 b.arg(b0)
14337 .arg(b1)
14338 .arg(b2)
14339 .arg(aq)
14340 .arg(ad)
14341 .arg(&mut y0)
14342 .arg(&mut y1)
14343 .arg(&mut y2)
14344 .arg(&inf)
14345 .arg(&o0)
14346 .arg(&o1)
14347 .arg(&o2)
14348 .arg(&rbl);
14349 unsafe {
14350 b.launch(cfg)?;
14351 }
14352 Ok((y0, y1, y2))
14353 }
14354
14355 #[allow(clippy::too_many_arguments)]
14357 pub fn qmatvec_q8_fused3_raw(
14358 &self,
14359 b0: &CudaSlice<u8>,
14360 b1: &CudaSlice<u8>,
14361 b2: &CudaSlice<u8>,
14362 x: &CudaSlice<f32>,
14363 in_f: usize,
14364 out0: usize,
14365 out1: usize,
14366 out2: usize,
14367 row_bytes: usize,
14368 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14369 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
14370 self.q8_fused3_core(b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes)
14371 }
14372
14373 pub fn matmul_q8_fused2_t(
14384 &self,
14385 w0: &crate::model::GpuTensor,
14386 w1: &crate::model::GpuTensor,
14387 aq: &CudaSlice<i8>,
14388 ad: &CudaSlice<f32>,
14389 m: usize,
14390 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>> {
14391 if !(2..=8).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
14395 return Ok(None);
14396 }
14397 if let Some([p0, p1]) = self.e4m3_fused_params(&[w0, w1]) {
14400 if m > 4 && !Self::b8_enabled() {
14401 return Ok(None);
14402 }
14403 return Ok(Some(self.e4m3_fused2_t_core(
14404 p0.0,
14405 p1.0,
14406 aq,
14407 ad,
14408 m,
14409 w0.in_features(),
14410 p0.1,
14411 p1.1,
14412 p0.2,
14413 p0.3,
14414 p1.3,
14415 )?));
14416 }
14417 let Some([p0, p1]) = self.q8_fused_params(&[w0, w1]) else {
14418 return Ok(None);
14419 };
14420 Ok(Some(self.q8_fused2_t_core(
14421 p0.0,
14422 p1.0,
14423 aq,
14424 ad,
14425 m,
14426 w0.in_features(),
14427 p0.1,
14428 p1.1,
14429 p0.2,
14430 )?))
14431 }
14432
14433 #[allow(clippy::too_many_arguments)]
14434 fn q8_fused2_t_core(
14435 &self,
14436 b0: &CudaSlice<u8>,
14437 b1: &CudaSlice<u8>,
14438 aq: &CudaSlice<i8>,
14439 ad: &CudaSlice<f32>,
14440 m: usize,
14441 in_f: usize,
14442 out0: usize,
14443 out1: usize,
14444 row_bytes: usize,
14445 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14446 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
14448 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
14449 let f = self.func(match Self::batched_mcols(m) {
14450 2 => "qmatvec_q8_0_mmvq_fused2_b2",
14451 4 => "qmatvec_q8_0_mmvq_fused2_b4",
14452 _ => "qmatvec_q8_0_mmvq_fused2_b8",
14454 });
14455 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
14456 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
14457 let cfg = LaunchConfig {
14458 grid_dim: (nb0 + nb1, 1, 1),
14459 block_dim: (32, ROWS_PER_BLOCK, 1),
14460 shared_mem_bytes: 0,
14461 };
14462 let (inf, o0, o1, mi, rbl) = (
14463 in_f as i32,
14464 out0 as i32,
14465 out1 as i32,
14466 m as i32,
14467 row_bytes as i64,
14468 );
14469 let __s_b = self.gpu.stream();
14470 let mut b = __s_b.launch_builder(&f);
14471 b.arg(b0)
14472 .arg(b1)
14473 .arg(aq)
14474 .arg(ad)
14475 .arg(&mut y0)
14476 .arg(&mut y1)
14477 .arg(&inf)
14478 .arg(&o0)
14479 .arg(&o1)
14480 .arg(&mi)
14481 .arg(&rbl);
14482 unsafe {
14483 b.launch(cfg)?;
14484 }
14485 Ok((y0, y1))
14486 }
14487
14488 #[allow(clippy::too_many_arguments)]
14491 pub fn qmatvec_q8_fused2_t_raw(
14492 &self,
14493 b0: &CudaSlice<u8>,
14494 b1: &CudaSlice<u8>,
14495 x: &CudaSlice<f32>,
14496 m: usize,
14497 in_f: usize,
14498 out0: usize,
14499 out1: usize,
14500 row_bytes: usize,
14501 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14502 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14503 self.q8_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes)
14504 }
14505
14506 #[allow(clippy::too_many_arguments)]
14509 pub fn matmul_q8_fused3_t(
14510 &self,
14511 w0: &crate::model::GpuTensor,
14512 w1: &crate::model::GpuTensor,
14513 w2: &crate::model::GpuTensor,
14514 aq: &CudaSlice<i8>,
14515 ad: &CudaSlice<f32>,
14516 m: usize,
14517 ) -> Result<Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>, Box<dyn std::error::Error>>
14518 {
14519 if !(2..=4).contains(&m) || std::env::var("MEMRA_NO_BATCHED").is_ok() {
14520 return Ok(None);
14521 }
14522 if let Some([p0, p1, p2]) = self.e4m3_fused_params(&[w0, w1, w2]) {
14523 return Ok(Some(self.e4m3_fused3_t_core(
14524 p0.0,
14525 p1.0,
14526 p2.0,
14527 aq,
14528 ad,
14529 m,
14530 w0.in_features(),
14531 p0.1,
14532 p1.1,
14533 p2.1,
14534 p0.2,
14535 p0.3,
14536 p1.3,
14537 p2.3,
14538 )?));
14539 }
14540 let Some([p0, p1, p2]) = self.q8_fused_params(&[w0, w1, w2]) else {
14541 return Ok(None);
14542 };
14543 Ok(Some(self.q8_fused3_t_core(
14544 p0.0,
14545 p1.0,
14546 p2.0,
14547 aq,
14548 ad,
14549 m,
14550 w0.in_features(),
14551 p0.1,
14552 p1.1,
14553 p2.1,
14554 p0.2,
14555 )?))
14556 }
14557
14558 #[allow(clippy::too_many_arguments)]
14559 fn q8_fused3_t_core(
14560 &self,
14561 b0: &CudaSlice<u8>,
14562 b1: &CudaSlice<u8>,
14563 b2: &CudaSlice<u8>,
14564 aq: &CudaSlice<i8>,
14565 ad: &CudaSlice<f32>,
14566 m: usize,
14567 in_f: usize,
14568 out0: usize,
14569 out1: usize,
14570 out2: usize,
14571 row_bytes: usize,
14572 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14573 const ROWS_PER_BLOCK: u32 = 4;
14574 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
14575 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
14576 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
14577 let f = self.func(if Self::batched_mcols(m) == 2 {
14578 "qmatvec_q8_0_mmvq_fused3_b2"
14579 } else {
14580 "qmatvec_q8_0_mmvq_fused3_b4"
14581 });
14582 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
14583 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
14584 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
14585 let cfg = LaunchConfig {
14586 grid_dim: (nb0 + nb1 + nb2, 1, 1),
14587 block_dim: (32, ROWS_PER_BLOCK, 1),
14588 shared_mem_bytes: 0,
14589 };
14590 let (inf, o0, o1, o2, mi, rbl) = (
14591 in_f as i32,
14592 out0 as i32,
14593 out1 as i32,
14594 out2 as i32,
14595 m as i32,
14596 row_bytes as i64,
14597 );
14598 let __s_b = self.gpu.stream();
14599 let mut b = __s_b.launch_builder(&f);
14600 b.arg(b0)
14601 .arg(b1)
14602 .arg(b2)
14603 .arg(aq)
14604 .arg(ad)
14605 .arg(&mut y0)
14606 .arg(&mut y1)
14607 .arg(&mut y2)
14608 .arg(&inf)
14609 .arg(&o0)
14610 .arg(&o1)
14611 .arg(&o2)
14612 .arg(&mi)
14613 .arg(&rbl);
14614 unsafe {
14615 b.launch(cfg)?;
14616 }
14617 Ok((y0, y1, y2))
14618 }
14619
14620 #[allow(clippy::too_many_arguments)]
14622 pub fn qmatvec_q8_fused3_t_raw(
14623 &self,
14624 b0: &CudaSlice<u8>,
14625 b1: &CudaSlice<u8>,
14626 b2: &CudaSlice<u8>,
14627 x: &CudaSlice<f32>,
14628 m: usize,
14629 in_f: usize,
14630 out0: usize,
14631 out1: usize,
14632 out2: usize,
14633 row_bytes: usize,
14634 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14635 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
14636 self.q8_fused3_t_core(b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes)
14637 }
14638
14639 pub fn q8_ffn_fuse2_on(&self) -> bool {
14643 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14644 *ON.get_or_init(|| std::env::var("MEMRA_Q8_FFN_FUSE2").as_deref() != Ok("0"))
14645 }
14646
14647 #[allow(clippy::type_complexity)]
14653 fn q8_fused_params<'w, const N: usize>(
14654 &self,
14655 ws: &[&'w crate::model::GpuTensor; N],
14656 ) -> Option<[(&'w CudaSlice<u8>, usize, usize); N]> {
14657 use crate::model::GpuTensor;
14658 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
14659 return None;
14660 }
14661 if std::env::var("MEMRA_Q8_DUAL").is_ok_and(|v| v == "0") {
14662 return None;
14663 }
14664 let in_f = ws[0].in_features();
14665 let mut out: [Option<(&CudaSlice<u8>, usize, usize)>; N] = [None; N];
14666 for (i, w) in ws.iter().enumerate() {
14667 match w {
14668 GpuTensor::Quant {
14669 bytes,
14670 qtype,
14671 row_bytes,
14672 scale,
14673 ..
14674 } if *qtype == QT_Q8_0 && *scale == 1.0 && w.in_features() == in_f => {
14675 out[i] = Some((bytes, w.out_features(), *row_bytes))
14676 }
14677 _ => return None,
14678 }
14679 }
14680 Some(out.map(|o| o.unwrap()))
14681 }
14682
14683 pub fn e4m3_dual_on(&self) -> bool {
14686 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
14687 *ON.get_or_init(|| std::env::var("MEMRA_E4M3_DUAL").as_deref() != Ok("0"))
14688 }
14689
14690 #[allow(clippy::type_complexity)]
14702 fn e4m3_fused_params<'w, const N: usize>(
14703 &self,
14704 ws: &[&'w crate::model::GpuTensor; N],
14705 ) -> Option<[(&'w CudaSlice<u8>, usize, usize, f32); N]> {
14706 use crate::model::GpuTensor;
14707 if !self.e4m3_dual_on() {
14708 return None;
14709 }
14710 let in_f = ws[0].in_features();
14711 let mut out: [Option<(&CudaSlice<u8>, usize, usize, f32)>; N] = [None; N];
14712 for (i, w) in ws.iter().enumerate() {
14713 match w {
14714 GpuTensor::Quant {
14715 bytes,
14716 qtype,
14717 row_bytes,
14718 scale,
14719 rp,
14720 rp4,
14721 ..
14722 } if *qtype == QT_F8_E4M3
14723 && w.in_features() == in_f
14724 && *row_bytes == in_f
14725 && !*rp
14726 && rp4.is_none() =>
14727 {
14728 out[i] = Some((bytes, w.out_features(), *row_bytes, *scale))
14729 }
14730 _ => return None,
14731 }
14732 }
14733 Some(out.map(|o| o.unwrap()))
14734 }
14735
14736 #[allow(clippy::too_many_arguments)]
14740 fn e4m3_fused2_core(
14741 &self,
14742 b0: &CudaSlice<u8>,
14743 b1: &CudaSlice<u8>,
14744 aq: &CudaSlice<i8>,
14745 ad: &CudaSlice<f32>,
14746 in_f: usize,
14747 out0: usize,
14748 out1: usize,
14749 row_bytes: usize,
14750 ws0: f32,
14751 ws1: f32,
14752 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14753 const ROWS_PER_BLOCK: u32 = 4; let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
14755 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
14756 let f = self.func("qmatvec_e4m3_mmvq_fused2");
14757 let mut y0 = self.alloc_uninit::<f32>(out0)?;
14758 let mut y1 = self.alloc_uninit::<f32>(out1)?;
14759 let cfg = LaunchConfig {
14760 grid_dim: (nb0 + nb1, 1, 1),
14761 block_dim: (32, ROWS_PER_BLOCK, 1),
14762 shared_mem_bytes: 0,
14763 };
14764 let (inf, o0, o1, rbl) = (in_f as i32, out0 as i32, out1 as i32, row_bytes as i64);
14765 let __s_b = self.gpu.stream();
14766 let mut b = __s_b.launch_builder(&f);
14767 b.arg(b0)
14768 .arg(b1)
14769 .arg(aq)
14770 .arg(ad)
14771 .arg(&mut y0)
14772 .arg(&mut y1)
14773 .arg(&inf)
14774 .arg(&o0)
14775 .arg(&o1)
14776 .arg(&rbl)
14777 .arg(&ws0)
14778 .arg(&ws1);
14779 unsafe {
14780 b.launch(cfg)?;
14781 }
14782 Ok((y0, y1))
14783 }
14784
14785 #[allow(clippy::too_many_arguments)]
14787 fn e4m3_fused3_core(
14788 &self,
14789 b0: &CudaSlice<u8>,
14790 b1: &CudaSlice<u8>,
14791 b2: &CudaSlice<u8>,
14792 aq: &CudaSlice<i8>,
14793 ad: &CudaSlice<f32>,
14794 in_f: usize,
14795 out0: usize,
14796 out1: usize,
14797 out2: usize,
14798 row_bytes: usize,
14799 ws0: f32,
14800 ws1: f32,
14801 ws2: f32,
14802 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14803 const ROWS_PER_BLOCK: u32 = 4;
14804 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
14805 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
14806 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
14807 let f = self.func("qmatvec_e4m3_mmvq_fused3");
14808 let mut y0 = self.alloc_uninit::<f32>(out0)?;
14809 let mut y1 = self.alloc_uninit::<f32>(out1)?;
14810 let mut y2 = self.alloc_uninit::<f32>(out2)?;
14811 let cfg = LaunchConfig {
14812 grid_dim: (nb0 + nb1 + nb2, 1, 1),
14813 block_dim: (32, ROWS_PER_BLOCK, 1),
14814 shared_mem_bytes: 0,
14815 };
14816 let (inf, o0, o1, o2, rbl) = (
14817 in_f as i32,
14818 out0 as i32,
14819 out1 as i32,
14820 out2 as i32,
14821 row_bytes as i64,
14822 );
14823 let __s_b = self.gpu.stream();
14824 let mut b = __s_b.launch_builder(&f);
14825 b.arg(b0)
14826 .arg(b1)
14827 .arg(b2)
14828 .arg(aq)
14829 .arg(ad)
14830 .arg(&mut y0)
14831 .arg(&mut y1)
14832 .arg(&mut y2)
14833 .arg(&inf)
14834 .arg(&o0)
14835 .arg(&o1)
14836 .arg(&o2)
14837 .arg(&rbl)
14838 .arg(&ws0)
14839 .arg(&ws1)
14840 .arg(&ws2);
14841 unsafe {
14842 b.launch(cfg)?;
14843 }
14844 Ok((y0, y1, y2))
14845 }
14846
14847 #[allow(clippy::too_many_arguments)]
14851 fn e4m3_fused2_t_core(
14852 &self,
14853 b0: &CudaSlice<u8>,
14854 b1: &CudaSlice<u8>,
14855 aq: &CudaSlice<i8>,
14856 ad: &CudaSlice<f32>,
14857 m: usize,
14858 in_f: usize,
14859 out0: usize,
14860 out1: usize,
14861 row_bytes: usize,
14862 ws0: f32,
14863 ws1: f32,
14864 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14865 const ROWS_PER_BLOCK: u32 = 4;
14866 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
14867 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
14868 let f = self.func(match Self::batched_mcols(m) {
14869 2 => "qmatvec_e4m3_mmvq_fused2_b2",
14870 4 => "qmatvec_e4m3_mmvq_fused2_b4",
14871 _ => "qmatvec_e4m3_mmvq_fused2_b8",
14872 });
14873 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
14874 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
14875 let cfg = LaunchConfig {
14876 grid_dim: (nb0 + nb1, 1, 1),
14877 block_dim: (32, ROWS_PER_BLOCK, 1),
14878 shared_mem_bytes: 0,
14879 };
14880 let (inf, o0, o1, mi, rbl) = (
14881 in_f as i32,
14882 out0 as i32,
14883 out1 as i32,
14884 m as i32,
14885 row_bytes as i64,
14886 );
14887 let __s_b = self.gpu.stream();
14888 let mut b = __s_b.launch_builder(&f);
14889 b.arg(b0)
14890 .arg(b1)
14891 .arg(aq)
14892 .arg(ad)
14893 .arg(&mut y0)
14894 .arg(&mut y1)
14895 .arg(&inf)
14896 .arg(&o0)
14897 .arg(&o1)
14898 .arg(&mi)
14899 .arg(&rbl);
14900 unsafe {
14901 b.launch(cfg)?;
14902 }
14903 if ws0 != 1.0 {
14904 self.scale_inplace(&mut y0, ws0, m * out0)?;
14905 }
14906 if ws1 != 1.0 {
14907 self.scale_inplace(&mut y1, ws1, m * out1)?;
14908 }
14909 Ok((y0, y1))
14910 }
14911
14912 #[allow(clippy::too_many_arguments)]
14914 fn e4m3_fused3_t_core(
14915 &self,
14916 b0: &CudaSlice<u8>,
14917 b1: &CudaSlice<u8>,
14918 b2: &CudaSlice<u8>,
14919 aq: &CudaSlice<i8>,
14920 ad: &CudaSlice<f32>,
14921 m: usize,
14922 in_f: usize,
14923 out0: usize,
14924 out1: usize,
14925 out2: usize,
14926 row_bytes: usize,
14927 ws0: f32,
14928 ws1: f32,
14929 ws2: f32,
14930 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
14931 const ROWS_PER_BLOCK: u32 = 4;
14932 let nb0 = (out0 as u32).div_ceil(ROWS_PER_BLOCK);
14933 let nb1 = (out1 as u32).div_ceil(ROWS_PER_BLOCK);
14934 let nb2 = (out2 as u32).div_ceil(ROWS_PER_BLOCK);
14935 let f = self.func(if Self::batched_mcols(m) == 2 {
14936 "qmatvec_e4m3_mmvq_fused3_b2"
14937 } else {
14938 "qmatvec_e4m3_mmvq_fused3_b4"
14939 });
14940 let mut y0 = self.alloc_uninit::<f32>(m * out0)?;
14941 let mut y1 = self.alloc_uninit::<f32>(m * out1)?;
14942 let mut y2 = self.alloc_uninit::<f32>(m * out2)?;
14943 let cfg = LaunchConfig {
14944 grid_dim: (nb0 + nb1 + nb2, 1, 1),
14945 block_dim: (32, ROWS_PER_BLOCK, 1),
14946 shared_mem_bytes: 0,
14947 };
14948 let (inf, o0, o1, o2, mi, rbl) = (
14949 in_f as i32,
14950 out0 as i32,
14951 out1 as i32,
14952 out2 as i32,
14953 m as i32,
14954 row_bytes as i64,
14955 );
14956 let __s_b = self.gpu.stream();
14957 let mut b = __s_b.launch_builder(&f);
14958 b.arg(b0)
14959 .arg(b1)
14960 .arg(b2)
14961 .arg(aq)
14962 .arg(ad)
14963 .arg(&mut y0)
14964 .arg(&mut y1)
14965 .arg(&mut y2)
14966 .arg(&inf)
14967 .arg(&o0)
14968 .arg(&o1)
14969 .arg(&o2)
14970 .arg(&mi)
14971 .arg(&rbl);
14972 unsafe {
14973 b.launch(cfg)?;
14974 }
14975 if ws0 != 1.0 {
14976 self.scale_inplace(&mut y0, ws0, m * out0)?;
14977 }
14978 if ws1 != 1.0 {
14979 self.scale_inplace(&mut y1, ws1, m * out1)?;
14980 }
14981 if ws2 != 1.0 {
14982 self.scale_inplace(&mut y2, ws2, m * out2)?;
14983 }
14984 Ok((y0, y1, y2))
14985 }
14986
14987 pub fn qmatvec_e4m3_blk_mmvq(
14997 &self,
14998 bytes: &CudaSlice<u8>,
14999 aq: &CudaSlice<i8>,
15000 ad: &CudaSlice<f32>,
15001 scales: &CudaSlice<f32>,
15002 m: usize,
15003 in_f: usize,
15004 out_f: usize,
15005 row_bytes: usize,
15006 scale_cols: usize,
15007 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15008 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_e4m3_blk_mmvq_into(
15010 bytes, aq, ad, scales, m, in_f, out_f, row_bytes, scale_cols, &mut y,
15011 )?;
15012 Ok(y)
15013 }
15014
15015 #[allow(clippy::too_many_arguments)]
15017 pub fn qmatvec_e4m3_blk_mmvq_into(
15018 &self,
15019 bytes: &CudaSlice<u8>,
15020 aq: &CudaSlice<i8>,
15021 ad: &CudaSlice<f32>,
15022 scales: &CudaSlice<f32>,
15023 m: usize,
15024 in_f: usize,
15025 out_f: usize,
15026 row_bytes: usize,
15027 scale_cols: usize,
15028 y: &mut CudaSlice<f32>,
15029 ) -> Result<(), Box<dyn std::error::Error>> {
15030 const ROWS_PER_BLOCK: u32 = 4; let f = self.func("qmatvec_e4m3_blk_mmvq");
15032 let cfg = LaunchConfig {
15033 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), m as u32, 1),
15034 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
15037 let (inf, outf, mi, rb, sc) = (
15038 in_f as i32,
15039 out_f as i32,
15040 m as i32,
15041 row_bytes as i64,
15042 scale_cols as i32,
15043 );
15044 let __s_b = self.gpu.stream();
15045 let mut b = __s_b.launch_builder(&f);
15046 b.arg(bytes)
15047 .arg(aq)
15048 .arg(ad)
15049 .arg(scales)
15050 .arg(&mut *y)
15051 .arg(&inf)
15052 .arg(&outf)
15053 .arg(&mi)
15054 .arg(&rb)
15055 .arg(&sc);
15056 unsafe {
15057 b.launch(cfg)?;
15058 }
15059 Ok(())
15060 }
15061
15062 #[allow(clippy::too_many_arguments)]
15068 pub fn qmatvec_e4m3_blk_mmvq_batched(
15069 &self,
15070 bytes: &CudaSlice<u8>,
15071 aq: &CudaSlice<i8>,
15072 ad: &CudaSlice<f32>,
15073 scales: &CudaSlice<f32>,
15074 m: usize,
15075 in_f: usize,
15076 out_f: usize,
15077 row_bytes: usize,
15078 scale_cols: usize,
15079 mcols: usize,
15080 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15081 const ROWS_PER_BLOCK: u32 = 4; debug_assert!(mcols >= m, "blk batched: mcols {mcols} < m {m}");
15083 let name = match mcols {
15084 2 => "qmatvec_e4m3_blk_mmvq_b2",
15085 4 => "qmatvec_e4m3_blk_mmvq_b4",
15086 8 => "qmatvec_e4m3_blk_mmvq_b8",
15087 16 => "qmatvec_e4m3_blk_mmvq_b16",
15088 _ => {
15089 return Err(
15090 format!("qmatvec_e4m3_blk_mmvq_batched: no kernel for mcols {mcols}").into(),
15091 );
15092 }
15093 };
15094 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
15095 let f = self.func(name);
15096 let cfg = LaunchConfig {
15097 grid_dim: ((out_f as u32).div_ceil(ROWS_PER_BLOCK), 1, 1),
15098 block_dim: (32, ROWS_PER_BLOCK, 1),
15099 shared_mem_bytes: 0,
15100 };
15101 let (inf, outf, mi, rb, sc) = (
15102 in_f as i32,
15103 out_f as i32,
15104 m as i32,
15105 row_bytes as i64,
15106 scale_cols as i32,
15107 );
15108 let __s_b = self.gpu.stream();
15109 let mut b = __s_b.launch_builder(&f);
15110 b.arg(bytes)
15111 .arg(aq)
15112 .arg(ad)
15113 .arg(scales)
15114 .arg(&mut y)
15115 .arg(&inf)
15116 .arg(&outf)
15117 .arg(&mi)
15118 .arg(&rb)
15119 .arg(&sc);
15120 unsafe {
15121 b.launch(cfg)?;
15122 }
15123 Ok(y)
15124 }
15125
15126 #[allow(clippy::too_many_arguments)]
15129 pub fn qmatvec_e4m3_blk_batched_raw(
15130 &self,
15131 bytes: &CudaSlice<u8>,
15132 x: &CudaSlice<f32>,
15133 scales: &CudaSlice<f32>,
15134 m: usize,
15135 in_f: usize,
15136 out_f: usize,
15137 row_bytes: usize,
15138 scale_cols: usize,
15139 mcols: usize,
15140 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15141 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15142 self.qmatvec_e4m3_blk_mmvq_batched(
15143 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols, mcols,
15144 )
15145 }
15146
15147 #[allow(clippy::too_many_arguments)]
15150 pub fn qmatvec_e4m3_blk_mmvq_raw(
15151 &self,
15152 bytes: &CudaSlice<u8>,
15153 x: &CudaSlice<f32>,
15154 scales: &CudaSlice<f32>,
15155 m: usize,
15156 in_f: usize,
15157 out_f: usize,
15158 row_bytes: usize,
15159 scale_cols: usize,
15160 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15161 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15162 self.qmatvec_e4m3_blk_mmvq(
15163 bytes, &aq, &ad, scales, m, in_f, out_f, row_bytes, scale_cols,
15164 )
15165 }
15166
15167 #[allow(clippy::too_many_arguments)]
15170 pub fn qmatvec_e4m3_fused2_raw(
15171 &self,
15172 b0: &CudaSlice<u8>,
15173 b1: &CudaSlice<u8>,
15174 x: &CudaSlice<f32>,
15175 in_f: usize,
15176 out0: usize,
15177 out1: usize,
15178 row_bytes: usize,
15179 ws0: f32,
15180 ws1: f32,
15181 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15182 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
15183 self.e4m3_fused2_core(b0, b1, &aq, &ad, in_f, out0, out1, row_bytes, ws0, ws1)
15184 }
15185
15186 #[allow(clippy::too_many_arguments)]
15187 pub fn qmatvec_e4m3_fused3_raw(
15188 &self,
15189 b0: &CudaSlice<u8>,
15190 b1: &CudaSlice<u8>,
15191 b2: &CudaSlice<u8>,
15192 x: &CudaSlice<f32>,
15193 in_f: usize,
15194 out0: usize,
15195 out1: usize,
15196 out2: usize,
15197 row_bytes: usize,
15198 ws0: f32,
15199 ws1: f32,
15200 ws2: f32,
15201 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15202 let (aq, ad) = self.quantize_q8_1(x, 1, in_f)?;
15203 self.e4m3_fused3_core(
15204 b0, b1, b2, &aq, &ad, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
15205 )
15206 }
15207
15208 #[allow(clippy::too_many_arguments)]
15209 pub fn qmatvec_e4m3_fused2_t_raw(
15210 &self,
15211 b0: &CudaSlice<u8>,
15212 b1: &CudaSlice<u8>,
15213 x: &CudaSlice<f32>,
15214 m: usize,
15215 in_f: usize,
15216 out0: usize,
15217 out1: usize,
15218 row_bytes: usize,
15219 ws0: f32,
15220 ws1: f32,
15221 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15222 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15223 self.e4m3_fused2_t_core(b0, b1, &aq, &ad, m, in_f, out0, out1, row_bytes, ws0, ws1)
15224 }
15225
15226 #[allow(clippy::too_many_arguments)]
15227 pub fn qmatvec_e4m3_fused3_t_raw(
15228 &self,
15229 b0: &CudaSlice<u8>,
15230 b1: &CudaSlice<u8>,
15231 b2: &CudaSlice<u8>,
15232 x: &CudaSlice<f32>,
15233 m: usize,
15234 in_f: usize,
15235 out0: usize,
15236 out1: usize,
15237 out2: usize,
15238 row_bytes: usize,
15239 ws0: f32,
15240 ws1: f32,
15241 ws2: f32,
15242 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
15243 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15244 self.e4m3_fused3_t_core(
15245 b0, b1, b2, &aq, &ad, m, in_f, out0, out1, out2, row_bytes, ws0, ws1, ws2,
15246 )
15247 }
15248
15249 fn try_e4m3_blk_pre(
15260 &self,
15261 w: &crate::model::GpuTensor,
15262 aq: &CudaSlice<i8>,
15263 ad: &CudaSlice<f32>,
15264 m: usize,
15265 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
15266 use crate::model::GpuTensor;
15267 if let GpuTensor::Quant {
15268 bytes,
15269 qtype,
15270 row_bytes,
15271 blk: Some(g),
15272 ..
15273 } = w
15274 {
15275 if *qtype == QT_F8_E4M3_BLK {
15276 if (2..=16).contains(&m)
15282 && std::env::var("MEMRA_NO_BATCHED").is_err()
15283 && (m <= 4 || Self::b8_enabled())
15284 {
15285 let mcols = Self::batched_mcols(m);
15286 return Ok(Some(self.qmatvec_e4m3_blk_mmvq_batched(
15287 bytes,
15288 aq,
15289 ad,
15290 &g.scales,
15291 m,
15292 w.in_features(),
15293 w.out_features(),
15294 *row_bytes,
15295 g.cols,
15296 mcols,
15297 )?));
15298 }
15299 return Ok(Some(self.qmatvec_e4m3_blk_mmvq(
15300 bytes,
15301 aq,
15302 ad,
15303 &g.scales,
15304 m,
15305 w.in_features(),
15306 w.out_features(),
15307 *row_bytes,
15308 g.cols,
15309 )?));
15310 }
15311 }
15312 Ok(None)
15313 }
15314
15315 fn try_e4m3_blk_prefill(
15362 &self,
15363 w: &crate::model::GpuTensor,
15364 x: &CudaSlice<f32>,
15365 m: usize,
15366 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
15367 use crate::model::GpuTensor;
15368 let GpuTensor::Quant {
15369 bytes,
15370 qtype,
15371 blk: Some(g),
15372 ..
15373 } = w
15374 else {
15375 return Ok(None);
15376 };
15377 if *qtype != QT_F8_E4M3_BLK {
15378 return Ok(None);
15379 }
15380 if let Some(y) = self.try_fp8_blk_mmq(w, x, m)? {
15385 return Ok(Some(y));
15386 }
15387 let (in_f, out_f) = (w.in_features(), w.out_features());
15388 let slab = self.fp8_blk_dequant_q8_0_dev(bytes, &g.scales, out_f, in_f)?;
15389 let tmp = GpuTensor::Quant {
15390 bytes: slab,
15391 qtype: QT_Q8_0,
15392 row_bytes: in_f / 32 * 34,
15393 ne: vec![in_f as u64, out_f as u64],
15394 scale: 1.0,
15395 rp: false,
15396 #[cfg(memra_cutlass)]
15397 cutlass: None,
15398 fp8: None,
15399 blk: None,
15400 f16: None,
15401 rp4: None,
15402 };
15403 Ok(Some(self.matmul(&tmp, x, m)?))
15405 }
15406
15407 pub fn matmul_pre_noscale(
15408 &self,
15409 w: &crate::model::GpuTensor,
15410 aq: &CudaSlice<i8>,
15411 ad: &CudaSlice<f32>,
15412 m: usize,
15413 ) -> Result<Option<(CudaSlice<f32>, f32)>, Box<dyn std::error::Error>> {
15414 use crate::model::GpuTensor;
15415 if m == 1 {
15419 if let Some(y) = self.try_e4m3_blk_pre(w, aq, ad, m)? {
15420 return Ok(Some((y, 1.0)));
15421 }
15422 }
15423 if m != 1 || !self.uses_q8_1_fast(w) {
15425 return Ok(None);
15426 }
15427 let in_f = w.in_features();
15428 let out_f = w.out_features();
15429 let (bytes, qtype, row_bytes, scale, rp) = match w {
15430 GpuTensor::Quant {
15431 bytes,
15432 qtype,
15433 row_bytes,
15434 scale,
15435 rp,
15436 ..
15437 } => (bytes, *qtype, *row_bytes, *scale, *rp),
15438 _ => return Ok(None),
15439 };
15440 if self.mmvq_supports(qtype) {
15442 let (mbytes, mrp) = match w {
15444 GpuTensor::Quant { rp4: Some(m4), .. } => (m4, true),
15445 _ => (bytes, rp),
15446 };
15447 let y = self.qmatvec_mmvq(
15448 mbytes, aq, ad, m, in_f, out_f, qtype, row_bytes, 1.0, mrp,
15449 )?;
15450 return Ok(Some((y, scale)));
15451 }
15452 let name = match qtype {
15454 QT_Q8_0 => "qmatvec_q8_0_dp4a",
15455 QT_Q4_K => "qmatvec_q4_K_dp4a",
15456 QT_Q6_K => "qmatvec_q6_K_dp4a",
15457 QT_Q5_K => "qmatvec_q5_K_dp4a",
15458 QT_Q3_K => "qmatvec_q3_K_dp4a",
15459 QT_NVFP4 => {
15460 if rp {
15461 "qmatvec_nvfp4_dp4a_rp"
15462 } else {
15463 "qmatvec_nvfp4_dp4a"
15464 }
15465 }
15466 QT_IQ4_XS => "qmatvec_iq4_XS_dp4a",
15467 _ => return Ok(None),
15468 };
15469 let f = self.func(name);
15470 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
15471 let cfg = LaunchConfig {
15472 grid_dim: (out_f as u32, m as u32, 1),
15473 block_dim: (128, 1, 1),
15474 shared_mem_bytes: 0,
15475 };
15476 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
15477 let __s_b = self.gpu.stream();
15478 let mut b = __s_b.launch_builder(&f);
15479 b.arg(bytes)
15480 .arg(aq)
15481 .arg(ad)
15482 .arg(&mut y)
15483 .arg(&inf)
15484 .arg(&outf)
15485 .arg(&mi)
15486 .arg(&rb);
15487 unsafe {
15488 b.launch(cfg)?;
15489 }
15490 Ok(Some((y, scale)))
15491 }
15492
15493 pub fn mmvq_supports(&self, qtype: i32) -> bool {
15496 if qtype == QT_F8_E4M3 {
15501 return true;
15502 }
15503 if std::env::var("MEMRA_MMVQ").as_deref() == Ok("0") {
15504 return false;
15505 }
15506 matches!(
15507 qtype,
15508 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_Q4_0
15509 )
15510 }
15511
15512 pub fn qmatvec_mmvq(
15517 &self,
15518 bytes: &CudaSlice<u8>,
15519 aq: &CudaSlice<i8>,
15520 ad: &CudaSlice<f32>,
15521 m: usize,
15522 in_f: usize,
15523 out_f: usize,
15524 qtype: i32,
15525 row_bytes: usize,
15526 scale: f32,
15527 rp: bool,
15528 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15529 let mut y = self.alloc_uninit::<f32>(m * out_f)?; self.qmatvec_mmvq_into(
15531 bytes, aq, ad, m, in_f, out_f, qtype, row_bytes, scale, rp, &mut y,
15532 )?;
15533 Ok(y)
15534 }
15535
15536 #[allow(clippy::too_many_arguments)]
15538 pub fn qmatvec_mmvq_into(
15539 &self,
15540 bytes: &CudaSlice<u8>,
15541 aq: &CudaSlice<i8>,
15542 ad: &CudaSlice<f32>,
15543 m: usize,
15544 in_f: usize,
15545 out_f: usize,
15546 qtype: i32,
15547 row_bytes: usize,
15548 scale: f32,
15549 rp: bool,
15550 y: &mut CudaSlice<f32>,
15551 ) -> Result<(), Box<dyn std::error::Error>> {
15552 debug_assert!(y.len() >= m * out_f);
15553 const ROWS_PER_BLOCK: u32 = 4; if qtype == QT_Q8_0
15559 && rp
15560 && m == 1
15561 && out_f >= 64
15562 && (out_f as u32).div_ceil(ROWS_PER_BLOCK) < 4 * self.sm_count() as u32
15563 && {
15564 static G2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15565 *G2.get_or_init(|| std::env::var("MEMRA_Q80_G2").as_deref() != Ok("0"))
15566 }
15567 {
15568 let f = self.func("qmatvec_q8_0_mmvq_rp_g2");
15569 let cfg = LaunchConfig {
15570 grid_dim: ((out_f as u32).div_ceil(2), 1, 1),
15571 block_dim: (32, 2, 1),
15572 shared_mem_bytes: 0,
15573 };
15574 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, 1i32, row_bytes as i64);
15575 let __s_b = self.gpu.stream();
15576 let mut b = __s_b.launch_builder(&f);
15577 b.arg(bytes)
15578 .arg(aq)
15579 .arg(ad)
15580 .arg(&mut *y)
15581 .arg(&inf)
15582 .arg(&outf)
15583 .arg(&mi)
15584 .arg(&rb);
15585 unsafe {
15586 b.launch(cfg)?;
15587 }
15588 if scale != 1.0 {
15589 self.scale_inplace(y, scale, out_f)?;
15590 }
15591 return Ok(());
15592 }
15593 let mut mr: u32 = if m == 1 && (qtype == QT_NVFP4 || qtype == QT_Q5_K) {
15602 2
15603 } else {
15604 1
15605 };
15606 if m == 1 && qtype == QT_Q4_0 {
15611 static Q40MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
15612 mr = *Q40MR.get_or_init(|| {
15615 std::env::var("MEMRA_Q40_MR")
15616 .ok()
15617 .and_then(|v| v.parse().ok())
15618 .unwrap_or(1)
15619 });
15620 }
15621 let q5_mode = std::env::var("MEMRA_Q5K_ISSUE").ok();
15632 let q5_force = q5_mode.as_deref() == Some("2");
15633 let q5_il = qtype == QT_Q5_K
15636 && m == 1
15637 && (q5_force || q5_mode.as_deref().map(|v| v != "0").unwrap_or(true));
15638 if q5_il && !q5_force && out_f > 65536 {
15639 mr = 1;
15640 }
15641 if qtype == QT_Q4_0 && rp && mr != 1 {
15644 mr = 2;
15645 }
15646 if qtype == QT_Q8_0 && rp {
15650 static Q80MR: std::sync::OnceLock<u32> = std::sync::OnceLock::new();
15651 mr = *Q80MR.get_or_init(|| {
15652 std::env::var("MEMRA_Q80_MR")
15653 .ok()
15654 .and_then(|v| v.parse().ok())
15655 .unwrap_or(1)
15656 });
15657 }
15658 let name = match (qtype, mr, rp) {
15659 (QT_NVFP4, 2, false) => "qmatvec_nvfp4_mmvq_mr2",
15660 (QT_NVFP4, 2, true) => "qmatvec_nvfp4_mmvq_mr2_rp",
15661 (QT_NVFP4, _, true) => "qmatvec_nvfp4_mmvq_rp",
15662 (QT_Q4_0, 1, true) => "qmatvec_q4_0_mmvq_rp",
15663 (QT_Q4_0, _, true) => "qmatvec_q4_0_mmvq_mr2_rp",
15664 (QT_Q5_K, 2, _) => {
15665 if q5_il {
15666 "qmatvec_q5_K_mmvq_mr2_il"
15667 } else {
15668 "qmatvec_q5_K_mmvq_mr2"
15669 }
15670 }
15671 (QT_Q8_0, 2, true) => "qmatvec_q8_0_mmvq_mr2_rp",
15672 (QT_Q8_0, _, true)
15677 if in_f % 1024 == 0 && {
15678 static CA: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15679 *CA.get_or_init(|| std::env::var("MEMRA_Q80_CA").as_deref() == Ok("1"))
15680 } =>
15681 {
15682 "qmatvec_q8_0_mmvq_rpca"
15683 }
15684 (QT_Q8_0, _, true) => "qmatvec_q8_0_mmvq_rp",
15685 (QT_Q8_0, _, _) => "qmatvec_q8_0_mmvq",
15686 (QT_Q4_K, _, true) => "qmatvec_q4_K_mmvq_rp",
15690 (QT_Q6_K, _, true) => "qmatvec_q6_K_mmvq_rp",
15691 (QT_Q4_K, _, _) => "qmatvec_q4_K_mmvq",
15692 (QT_Q4_0, 2, false) => "qmatvec_q4_0_mmvq_mr2",
15693 (QT_Q4_0, _, false) => "qmatvec_q4_0_mmvq",
15694 (QT_Q5_K, _, _) => {
15695 if q5_il {
15696 "qmatvec_q5_K_mmvq_il"
15697 } else {
15698 "qmatvec_q5_K_mmvq"
15699 }
15700 }
15701 (QT_Q6_K, _, _) => "qmatvec_q6_K_mmvq",
15702 (QT_NVFP4, _, false) => "qmatvec_nvfp4_mmvq",
15703 (QT_F8_E4M3, _, _) => "qmatvec_e4m3_mmvq",
15704 _ => panic!("qmatvec_mmvq: qtype {qtype} has no MMVQ kernel"),
15705 };
15706 let f = self.func(name);
15707 let rows_per_block = ROWS_PER_BLOCK * mr;
15709 let cfg = LaunchConfig {
15710 grid_dim: (
15711 (out_f as u32 + rows_per_block - 1) / rows_per_block,
15712 m as u32,
15713 1,
15714 ),
15715 block_dim: (32, ROWS_PER_BLOCK, 1), shared_mem_bytes: 0, };
15718 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
15719 let __s_b = self.gpu.stream();
15720 let mut b = __s_b.launch_builder(&f);
15721 if qtype == QT_NVFP4 || qtype == QT_F8_E4M3 {
15726 if Self::pdl_on()
15729 && Self::pdl_mmvq_on()
15730 && Self::pdl_nvfp4q8_on()
15731 && name == "qmatvec_nvfp4_mmvq_mr2_rp"
15732 {
15733 use cudarc::driver::{DevicePtr, DevicePtrMut};
15734 let s = &self.gpu.stream();
15735 let (pw, _g0) = bytes.device_ptr(s);
15736 let (paq, _g1) = aq.device_ptr(s);
15737 let (pad, _g2) = ad.device_ptr(s);
15738 let (py, _g3) = y.device_ptr_mut(s);
15739 let mut ps = [
15740 &pw as *const _ as *mut std::ffi::c_void,
15741 &paq as *const _ as *mut _,
15742 &pad as *const _ as *mut _,
15743 &py as *const _ as *mut _,
15744 &inf as *const _ as *mut _,
15745 &outf as *const _ as *mut _,
15746 &mi as *const _ as *mut _,
15747 &rb as *const _ as *mut _,
15748 &scale as *const _ as *mut _,
15749 ];
15750 unsafe {
15751 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
15752 }
15753 return Ok(());
15754 }
15755 b.arg(bytes)
15756 .arg(aq)
15757 .arg(ad)
15758 .arg(&mut *y)
15759 .arg(&inf)
15760 .arg(&outf)
15761 .arg(&mi)
15762 .arg(&rb)
15763 .arg(&scale);
15764 unsafe {
15765 b.launch(cfg)?;
15766 }
15767 } else if Self::pdl_on()
15768 && Self::pdl_mmvq_on()
15769 && (matches!(
15770 name,
15771 "qmatvec_q4_0_mmvq_rp" | "qmatvec_q6_K_mmvq" | "qmatvec_q6_K_mmvq_rp"
15772 ) || (Self::pdl_nvfp4q8_on()
15773 && matches!(name, "qmatvec_q8_0_mmvq_rp" | "qmatvec_q8_0_mmvq_mr2_rp")))
15774 {
15775 {
15779 use cudarc::driver::{DevicePtr, DevicePtrMut};
15780 let s = &self.gpu.stream();
15781 let (pw, _g0) = bytes.device_ptr(s);
15782 let (paq, _g1) = aq.device_ptr(s);
15783 let (pad, _g2) = ad.device_ptr(s);
15784 let (py, _g3) = y.device_ptr_mut(s);
15785 let mut ps = [
15786 &pw as *const _ as *mut std::ffi::c_void,
15787 &paq as *const _ as *mut _,
15788 &pad as *const _ as *mut _,
15789 &py as *const _ as *mut _,
15790 &inf as *const _ as *mut _,
15791 &outf as *const _ as *mut _,
15792 &mi as *const _ as *mut _,
15793 &rb as *const _ as *mut _,
15794 ];
15795 unsafe {
15796 self.launch_pdl(name, cfg.grid_dim, cfg.block_dim, &mut ps)?;
15797 }
15798 }
15799 if scale != 1.0 {
15800 self.scale_inplace(y, scale, m * out_f)?;
15801 }
15802 } else {
15803 b.arg(bytes)
15804 .arg(aq)
15805 .arg(ad)
15806 .arg(&mut *y)
15807 .arg(&inf)
15808 .arg(&outf)
15809 .arg(&mi)
15810 .arg(&rb);
15811 unsafe {
15812 b.launch(cfg)?;
15813 }
15814 if scale != 1.0 {
15815 self.scale_inplace(y, scale, m * out_f)?;
15816 }
15817 }
15818 Ok(())
15819 }
15820
15821 pub fn qmatvec_mmvq_raw(
15825 &self,
15826 bytes: &CudaSlice<u8>,
15827 x: &CudaSlice<f32>,
15828 m: usize,
15829 in_f: usize,
15830 out_f: usize,
15831 qtype: i32,
15832 row_bytes: usize,
15833 rp: bool,
15834 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
15835 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
15836 self.qmatvec_mmvq(bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, 1.0, rp)
15837 }
15838
15839 pub fn batched_supports(&self, qtype: i32) -> bool {
15843 matches!(
15844 qtype,
15845 QT_Q8_0 | QT_Q4_K | QT_Q5_K | QT_Q6_K | QT_NVFP4 | QT_F8_E4M3 | QT_Q4_0
15846 )
15847 }
15848
15849 pub fn iq_fast_enabled() -> bool {
15857 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15858 *ON.get_or_init(|| {
15859 std::env::var("MEMRA_IQ_FAST")
15860 .map(|v| v != "0")
15861 .unwrap_or(true)
15862 })
15863 }
15864
15865 pub fn b8_enabled() -> bool {
15868 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
15869 *ON.get_or_init(|| std::env::var("MEMRA_B8").map(|v| v != "0").unwrap_or(true))
15870 }
15871
15872 pub fn batched_mcols(m: usize) -> usize {
15874 if m == 2 {
15875 2
15876 } else if m <= 4 {
15877 4
15878 } else if m <= 8 {
15879 8
15880 } else {
15881 16
15882 }
15883 }
15884
15885 fn batched_kernel_name(qtype: i32, mcols: usize) -> Option<&'static str> {
15890 Some(match (qtype, mcols) {
15891 (QT_Q8_0, 2) => "qmatvec_q8_0_mmvq_b2",
15892 (QT_Q8_0, 4) => "qmatvec_q8_0_mmvq_b4",
15893 (QT_Q8_0, 8) => "qmatvec_q8_0_mmvq_b8",
15894 (QT_Q8_0, 16) => "qmatvec_q8_0_mmvq_b16",
15900 (QT_Q4_K, 2) => "qmatvec_q4_K_mmvq_b2",
15901 (QT_Q4_K, 4) => "qmatvec_q4_K_mmvq_b4",
15902 (QT_Q4_K, 8) => "qmatvec_q4_K_mmvq_b8",
15903 (QT_Q4_K, 16) => "qmatvec_q4_K_mmvq_b16",
15906 (QT_Q5_K, 2) => "qmatvec_q5_K_mmvq_b2",
15907 (QT_Q5_K, 4) => "qmatvec_q5_K_mmvq_b4",
15908 (QT_Q5_K, 8) => "qmatvec_q5_K_mmvq_b8",
15909 (QT_Q5_K, 16) => "qmatvec_q5_K_mmvq_b16",
15912 (QT_Q6_K, 2) => "qmatvec_q6_K_mmvq_b2",
15913 (QT_Q6_K, 4) => "qmatvec_q6_K_mmvq_b4",
15914 (QT_Q6_K, 8) => "qmatvec_q6_K_mmvq_b8",
15915 (QT_Q6_K, 16) => "qmatvec_q6_K_mmvq_b16",
15916 (QT_NVFP4, 2) => "qmatvec_nvfp4_mmvq_b2",
15917 (QT_NVFP4, 4) => "qmatvec_nvfp4_mmvq_b4",
15918 (QT_NVFP4, 8) => "qmatvec_nvfp4_mmvq_b8",
15919 (QT_NVFP4, 16) => "qmatvec_nvfp4_mmvq_b16",
15923 (QT_F8_E4M3, 2) => "qmatvec_e4m3_mmvq_b2",
15924 (QT_F8_E4M3, 4) => "qmatvec_e4m3_mmvq_b4",
15925 (QT_F8_E4M3, 8) => "qmatvec_e4m3_mmvq_b8",
15926 (QT_F8_E4M3, 16) => "qmatvec_e4m3_mmvq_b16",
15930 (QT_Q4_0, 2) => "qmatvec_q4_0_mmvq_b2",
15931 (QT_Q4_0, 4) => "qmatvec_q4_0_mmvq_b4",
15932 (QT_Q4_0, 8) => "qmatvec_q4_0_mmvq_b8",
15933 (QT_Q4_0, 16) => "qmatvec_q4_0_mmvq_b16",
15934 _ => return None,
15935 })
15936 }
15937
15938 pub fn sm_count(&self) -> i32 {
15973 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
15974 *SMS.get_or_init(|| {
15975 use cudarc::driver::sys::CUdevice_attribute_enum as A;
15976 self.gpu
15977 .ctx
15978 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
15979 .unwrap_or(82)
15980 })
15981 }
15982
15983 pub fn batched_variant(
15984 &self,
15985 _m: usize,
15986 in_f: usize,
15987 out_f: usize,
15988 qtype: i32,
15989 row_bytes: usize,
15990 mcols: usize,
15991 rp: bool,
15992 ) -> &'static str {
15993 if qtype == QT_Q8_0 {
15998 return if rp { "rp" } else { "base" };
15999 }
16000 static BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
16001 let bv = *BV.get_or_init(|| match std::env::var("MEMRA_MMVQ_BV").as_deref() {
16002 Ok("base") => "base",
16003 Ok("pf") => "pf",
16004 Ok("r2") => "r2",
16005 Ok("r2w8") => "r2w8",
16006 Ok("pfr2") => "pfr2",
16007 Ok("ca") => "ca",
16008 Ok("car2") => "car2",
16009 Ok("rp") => "rp",
16012 Ok("rpr2") => "rpr2",
16013 Ok("rpr2w8") => "rpr2w8",
16014 Ok("rpca") => "rpca",
16017 Ok("rpcar2") => "rpcar2",
16018 Ok("rpsc") => "rpsc",
16025 Ok("rpms") => "rpms",
16026 Ok("rpmsc") => "rpmsc",
16027 Ok("rpks") => "rpks",
16028 Ok("rpksc") => "rpksc",
16029 _ => "auto",
16030 });
16031 let ca_ok = qtype == QT_NVFP4 && (row_bytes % 16 == 0) && (in_f % 1024 == 0);
16035 static KS_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16040 let ks_on = *KS_ON.get_or_init(|| std::env::var("MEMRA_KS").as_deref() != Ok("0"));
16041 let sc_ok = ks_on && qtype == QT_NVFP4 && (in_f % 256 == 0) && (in_f / 64 <= 272);
16042 let ks_ok = ks_on && qtype == QT_NVFP4 && (in_f % 512 == 0) && (in_f / 64 <= 272);
16043 static SMS: std::sync::OnceLock<i32> = std::sync::OnceLock::new();
16044 let sms = *SMS.get_or_init(|| {
16045 use cudarc::driver::sys::CUdevice_attribute_enum as A;
16046 self.gpu
16047 .ctx
16048 .attribute(A::CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT)
16049 .unwrap_or(82)
16050 });
16051 let kq_r2 = matches!(qtype, QT_Q4_K | QT_Q5_K | QT_Q6_K);
16071 static KQBV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
16074 let kq_bv = *KQBV.get_or_init(|| match std::env::var("MEMRA_KQ_BV").as_deref() {
16075 Ok("base") => "base",
16076 Ok("r2") => "r2",
16077 Ok("r2w8") => "r2w8",
16078 _ => "auto",
16079 });
16080 let variant: &'static str = if qtype == QT_Q4_0 {
16081 static Q40BV: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
16085 let q40 = *Q40BV.get_or_init(|| match std::env::var("MEMRA_Q40_BV").as_deref() {
16086 Ok("base") => "base",
16092 Ok("r2") => "r2",
16093 Ok("ms") => "ms",
16094 Ok("sm") => "sm",
16095 Ok("la") => "la",
16096 _ => "auto",
16097 });
16098 let v = if q40 != "auto" {
16099 q40
16100 } else if (out_f as u32).div_ceil(8) >= 4 * sms as u32 {
16101 "r2"
16102 } else {
16103 "base"
16104 };
16105 if rp {
16110 match v {
16111 "ms" => "r2ms_rp",
16112 "sm" => "r2sm_rp",
16113 "la" => "r2la_rp",
16114 "r2" => "r2_rp",
16115 _ => "rp",
16116 }
16117 } else if matches!(v, "ms" | "sm" | "la") {
16118 "r2"
16119 } else {
16120 v
16121 }
16122 } else if qtype != QT_NVFP4 && !kq_r2 {
16123 "base"
16124 } else if kq_r2 && rp {
16125 "rp"
16129 } else if kq_r2 {
16130 if kq_bv != "auto" {
16133 if kq_bv == "r2w8" && mcols != 4 {
16134 "r2"
16135 } else {
16136 kq_bv
16137 }
16138 } else if bv != "auto" {
16139 match bv {
16140 "r2" | "pfr2" | "rpr2" | "car2" => "r2",
16141 "r2w8" | "rpr2w8" => {
16142 if mcols != 4 {
16143 "r2"
16144 } else {
16145 "r2w8"
16146 }
16147 }
16148 _ => "base", }
16150 } else {
16151 let blocks = (out_f + 7) / 8;
16152 let waves = blocks as f64 / (7 * sms as usize) as f64;
16153 let filled = blocks >= 4 * sms as usize;
16154 let use_r2 = if qtype == QT_Q4_K {
16155 filled
16156 } else {
16157 waves >= 2.0
16158 };
16159 if use_r2 { "r2" } else { "base" }
16160 }
16161 } else if bv != "auto" {
16162 let v = if bv == "r2w8" && mcols == 2 {
16167 "r2"
16168 } else if bv == "ca" && (!ca_ok || mcols == 8) {
16169 "pf"
16170 } else if bv == "car2" && (!ca_ok || mcols == 8) {
16171 "r2"
16172 } else if bv == "pfr2" && mcols == 8 {
16173 "r2"
16174 } else if (bv == "rpr2w8" || bv == "rpr2") && mcols == 2 {
16175 "rpr2"
16176 }
16177 else if (bv == "rpca" || bv == "rpcar2") && (!ca_ok || mcols == 8) {
16179 if mcols == 8 { "rpr2w8" } else { "rpr2" }
16180 } else if bv == "rpcar2" && mcols == 2 {
16181 "rpca"
16182 }
16183 else if (bv == "rpsc" || bv == "rpmsc") && !sc_ok {
16186 "rpr2"
16187 } else if (bv == "rpks" || bv == "rpksc") && !ks_ok {
16188 "rpr2"
16189 } else {
16190 bv
16191 };
16192 if rp {
16193 match v {
16194 "base" | "pf" | "ca" | "rp" => "rp",
16195 "r2" | "pfr2" | "car2" | "rpr2" => "rpr2",
16196 "r2w8" | "rpr2w8" => {
16197 if mcols == 2 {
16198 "rpr2"
16199 } else {
16200 "rpr2w8"
16201 }
16202 }
16203 other => other, }
16205 } else {
16206 v
16207 }
16208 } else if mcols == 8 {
16209 if rp {
16220 if sc_ok { "rpsc" } else { "rpr2w8" }
16221 } else {
16222 "r2w8"
16223 }
16224 } else if mcols >= 4 {
16225 let blocks = (out_f + 7) / 8;
16229 let r7 = 7 * sms as usize;
16230 let r8 = 8 * sms as usize;
16231 let waves = blocks as f64 / r7 as f64;
16232 let filled = blocks >= 4 * sms as usize;
16233 if filled && blocks.div_ceil(r8) < blocks.div_ceil(r7) {
16237 if rp { "rpr2w8" } else { "r2w8" }
16241 } else if waves >= 2.0 || (waves <= 1.0 && filled) {
16242 if rp { "rpr2" } else { "r2" }
16245 } else {
16246 if rp { "rp" } else { "pf" }
16250 }
16251 } else if in_f >= 6144 {
16252 if rp { "rpr2" } else { "r2" }
16256 } else if rp {
16257 let waves = ((out_f + 7) / 8) as f64 / (7 * sms as usize) as f64;
16262 if sc_ok && waves >= 0.9 && waves <= 1.1 {
16263 "rpsc"
16264 } else {
16265 "rp"
16266 }
16267 } else {
16268 "base"
16269 };
16270 variant
16271 }
16272
16273 pub fn qmatvec_mmvq_batched(
16274 &self,
16275 bytes: &CudaSlice<u8>,
16276 aq: &CudaSlice<i8>,
16277 ad: &CudaSlice<f32>,
16278 m: usize,
16279 in_f: usize,
16280 out_f: usize,
16281 qtype: i32,
16282 row_bytes: usize,
16283 mcols: usize,
16284 scale: f32,
16285 rp: bool,
16286 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16287 const ROWS_PER_BLOCK: u32 = 4;
16288 let forced: Option<&'static str> = {
16293 static V: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
16294 V.get_or_init(|| std::env::var("MEMRA_BVAR").ok())
16295 .as_deref()
16296 .map(|s| Box::leak(s.to_string().into_boxed_str()) as &'static str)
16297 };
16298 let variant = match forced {
16299 Some(v) if !rp || v.contains("rp") => v,
16300 _ => self.batched_variant(m, in_f, out_f, qtype, row_bytes, mcols, rp),
16301 };
16302 let base_name = Self::batched_kernel_name(qtype, mcols).ok_or_else(|| {
16303 format!("qmatvec_mmvq_batched: no kernel for qtype {qtype} mcols {mcols}")
16304 })?;
16305 let variant = if mcols == 16 {
16309 if rp { "rp" } else { "base" }
16310 } else {
16311 variant
16312 };
16313 static B567: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
16320 let b567 = *B567.get_or_init(|| std::env::var("MEMRA_B567").as_deref() != Ok("0"));
16321 if b567
16322 && qtype == QT_NVFP4
16323 && rp
16324 && mcols == 8
16325 && (5..=7).contains(&m)
16326 && matches!(variant, "rpsc" | "rpr2w8")
16327 {
16328 let f = self.func(&format!("qmatvec_nvfp4_mmvq_b{m}_{variant}"));
16329 let rows_per_block = ROWS_PER_BLOCK * 2; let mut y = self.alloc_uninit::<f32>(m * out_f)?;
16331 let cfg = LaunchConfig {
16332 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
16333 block_dim: (32, ROWS_PER_BLOCK, 1),
16334 shared_mem_bytes: 0,
16335 };
16336 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
16337 let __s_b = self.gpu.stream();
16338 let mut b = __s_b.launch_builder(&f);
16339 b.arg(bytes)
16340 .arg(aq)
16341 .arg(ad)
16342 .arg(&mut y)
16343 .arg(&inf)
16344 .arg(&outf)
16345 .arg(&mi)
16346 .arg(&rb);
16347 unsafe {
16348 b.launch(cfg)?;
16349 }
16350 if scale != 1.0 {
16351 self.scale_inplace(&mut y, scale, m * out_f)?;
16352 }
16353 return Ok(y);
16354 }
16355 let (name, rows_per_block): (std::borrow::Cow<'static, str>, u32) = match variant {
16356 "base" => (base_name.into(), ROWS_PER_BLOCK),
16357 "pf" => (format!("{base_name}_pf").into(), ROWS_PER_BLOCK),
16358 "ca" => (format!("{base_name}_ca").into(), ROWS_PER_BLOCK),
16359 "rp" => (format!("{base_name}_rp").into(), ROWS_PER_BLOCK),
16360 "rpca" => (format!("{base_name}_rpca").into(), ROWS_PER_BLOCK), "rpks" => (format!("{base_name}_rpks").into(), ROWS_PER_BLOCK),
16364 "rpksc" => (format!("{base_name}_rpksc").into(), ROWS_PER_BLOCK),
16365 "rpms" => (format!("{base_name}_rpms").into(), ROWS_PER_BLOCK),
16366 "rpmsc" => (format!("{base_name}_rpmsc").into(), ROWS_PER_BLOCK),
16367 "r2ms_rp" => (format!("{base_name}_r2ms_rp").into(), ROWS_PER_BLOCK),
16368 "r2sm_rp" => (format!("{base_name}_r2sm_rp").into(), ROWS_PER_BLOCK * 2),
16369 "r2la_rp" => (format!("{base_name}_r2la_rp").into(), ROWS_PER_BLOCK * 2),
16370 v => (format!("{base_name}_{v}").into(), ROWS_PER_BLOCK * 2), };
16372 debug_assert!(
16373 !rp || name.contains("_rp"),
16374 "rp weight dispatched to a GGUF-layout kernel"
16375 );
16376 let f = self.func(&name);
16377 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
16378 let smem = if name.contains("_r2sm_rp") {
16380 (mcols * 32 * 9 * 4 + mcols * 32 * 4) as u32
16381 } else {
16382 0
16383 };
16384 let cfg = LaunchConfig {
16385 grid_dim: ((out_f as u32 + rows_per_block - 1) / rows_per_block, 1, 1),
16386 block_dim: (32, ROWS_PER_BLOCK, 1),
16387 shared_mem_bytes: smem,
16388 };
16389 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
16390 let __s_b = self.gpu.stream();
16391 let mut b = __s_b.launch_builder(&f);
16392 b.arg(bytes)
16393 .arg(aq)
16394 .arg(ad)
16395 .arg(&mut y)
16396 .arg(&inf)
16397 .arg(&outf)
16398 .arg(&mi)
16399 .arg(&rb);
16400 unsafe {
16401 b.launch(cfg)?;
16402 }
16403 if scale != 1.0 {
16404 self.scale_inplace(&mut y, scale, m * out_f)?;
16405 }
16406 Ok(y)
16407 }
16408
16409 pub fn qmatvec_batched_raw(
16413 &self,
16414 bytes: &CudaSlice<u8>,
16415 x: &CudaSlice<f32>,
16416 m: usize,
16417 in_f: usize,
16418 out_f: usize,
16419 qtype: i32,
16420 row_bytes: usize,
16421 mcols: usize,
16422 rp: bool,
16423 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16424 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
16425 self.qmatvec_mmvq_batched(
16426 bytes, &aq, &ad, m, in_f, out_f, qtype, row_bytes, mcols, 1.0, rp,
16427 )
16428 }
16429
16430 pub fn qmatvec_nvfp4_batched_raw(
16432 &self,
16433 bytes: &CudaSlice<u8>,
16434 x: &CudaSlice<f32>,
16435 m: usize,
16436 in_f: usize,
16437 out_f: usize,
16438 row_bytes: usize,
16439 mcols: usize,
16440 rp: bool,
16441 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16442 self.qmatvec_batched_raw(bytes, x, m, in_f, out_f, QT_NVFP4, row_bytes, mcols, rp)
16443 }
16444
16445 fn try_fp4_gemm(
16449 &self,
16450 w: &crate::model::GpuTensor,
16451 x: &CudaSlice<f32>,
16452 m: usize,
16453 in_f: usize,
16454 out_f: usize,
16455 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16456 use crate::model::GpuTensor;
16457 if cfg!(memra_portable_cuda) {
16458 return Ok(None);
16459 }
16460 if std::env::var("MEMRA_FP4").is_ok() {
16463 refuse_portable_force("MEMRA_FP4", "the sm_120a mxf4 block-scale MMA");
16464 }
16465 if std::env::var("MEMRA_FP4").is_err() {
16466 return Ok(None);
16467 }
16468 #[cfg(memra_cutlass)]
16477 if m >= 128 && std::env::var("MEMRA_FP4_CUTLASS").is_ok() {
16478 if let GpuTensor::Quant {
16479 bytes,
16480 qtype,
16481 scale,
16482 row_bytes,
16483 cutlass,
16484 ..
16485 } = w
16486 {
16487 if *qtype == QT_NVFP4 && in_f % 64 == 0 {
16488 if let Some(cw) = cutlass {
16489 let y = self.cutlass_fp4_gemm(
16491 &cw.b_packed,
16492 &cw.sfb_swizzled,
16493 x,
16494 *scale,
16495 m,
16496 out_f,
16497 in_f,
16498 )?;
16499 return Ok(Some(y));
16500 } else if std::env::var("MEMRA_FP4_CUTLASS_OTF").is_ok() {
16501 let (b_packed, sfb_sw) =
16506 self.build_cutlass_weight(bytes, out_f, in_f, *row_bytes)?;
16507 let y =
16508 self.cutlass_fp4_gemm(&b_packed, &sfb_sw, x, *scale, m, out_f, in_f)?;
16509 return Ok(Some(y));
16510 }
16511 }
16512 }
16513 }
16514 if let GpuTensor::Quant {
16515 bytes,
16516 qtype,
16517 row_bytes,
16518 scale,
16519 rp,
16520 ..
16521 } = w
16522 {
16523 if *qtype == QT_NVFP4 && in_f % 64 == 0 && !*rp {
16526 let y =
16527 self.qmatvec_gemm_nvfp4_fp4(bytes, x, m, in_f, out_f, *row_bytes, *scale)?;
16528 return Ok(Some(y));
16529 }
16530 }
16531 Ok(None)
16532 }
16533
16534 pub fn rms_norm_f16out(
16538 &self,
16539 x: &CudaSlice<f32>,
16540 w: &CudaSlice<f32>,
16541 dst: &mut CudaSlice<f32>,
16542 dst16: &mut CudaSlice<u8>,
16543 ncols: usize,
16544 nrows: usize,
16545 eps: f32,
16546 ) -> Result<(), Box<dyn std::error::Error>> {
16547 let f = self.func("rms_norm_f16out_f32");
16548 let cfg = LaunchConfig {
16549 grid_dim: (nrows as u32, 1, 1),
16550 block_dim: (rms_block(), 1, 1),
16551 shared_mem_bytes: 0,
16552 };
16553 let (nc, e) = (ncols as i32, eps);
16554 let __s_b = self.gpu.stream();
16555 let mut b = __s_b.launch_builder(&f);
16556 b.arg(x).arg(w).arg(dst).arg(dst16).arg(&nc).arg(&e);
16557 unsafe {
16558 b.launch(cfg)?;
16559 }
16560 Ok(())
16561 }
16562
16563 #[allow(clippy::too_many_arguments)]
16566 pub fn add_rms_norm_f16out(
16567 &self,
16568 a: &CudaSlice<f32>,
16569 b: &CudaSlice<f32>,
16570 w: &CudaSlice<f32>,
16571 res: &mut CudaSlice<f32>,
16572 dst: &mut CudaSlice<f32>,
16573 dst16: &mut CudaSlice<u8>,
16574 ncols: usize,
16575 nrows: usize,
16576 eps: f32,
16577 ) -> Result<(), Box<dyn std::error::Error>> {
16578 let f = self.func("add_rms_norm_f16out_f32");
16579 let cfg = LaunchConfig {
16580 grid_dim: (nrows as u32, 1, 1),
16581 block_dim: (rms_block(), 1, 1),
16582 shared_mem_bytes: 0,
16583 };
16584 let (nc, e) = (ncols as i32, eps);
16585 let __s_lb = self.gpu.stream();
16586 let mut lb = __s_lb.launch_builder(&f);
16587 lb.arg(a)
16588 .arg(b)
16589 .arg(w)
16590 .arg(res)
16591 .arg(dst)
16592 .arg(dst16)
16593 .arg(&nc)
16594 .arg(&e);
16595 unsafe {
16596 lb.launch(cfg)?;
16597 }
16598 Ok(())
16599 }
16600
16601 pub fn matmul_group_xh(
16604 &self,
16605 ws: &[&crate::model::GpuTensor],
16606 x: &CudaSlice<f32>,
16607 xh: &CudaSlice<u8>,
16608 m: usize,
16609 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16610 let mut out = Vec::with_capacity(ws.len());
16611 let in_f = ws[0].in_features();
16612 for w in ws {
16613 if w.in_features() == in_f && m >= 16 && !self.verify_exact_on() {
16614 if let Some(y) = self.try_f16_gemm_pre(w, xh, m)? {
16615 out.push(y);
16616 continue;
16617 }
16618 }
16619 out.push(self.matmul(w, x, m)?);
16620 }
16621 Ok(out)
16622 }
16623
16624 pub fn gdn_pad_mask(
16627 &self,
16628 beta: &mut CudaSlice<f32>,
16629 g_log: &mut CudaSlice<f32>,
16630 len_d: &CudaSlice<i32>,
16631 h: usize,
16632 t: usize,
16633 ) -> Result<(), Box<dyn std::error::Error>> {
16634 let f = self.func("gdn_pad_mask_f32");
16635 let cfg = LaunchConfig::for_num_elems((t * h) as u32);
16636 let (hi, ti) = (h as i32, t as i32);
16637 let __s_b = self.gpu.stream();
16638 let mut b = __s_b.launch_builder(&f);
16639 b.arg(beta).arg(g_log).arg(len_d).arg(&hi).arg(&ti);
16640 unsafe {
16641 b.launch(cfg)?;
16642 }
16643 Ok(())
16644 }
16645
16646 pub fn row_gather_dev(
16649 &self,
16650 src: &CudaSlice<f32>,
16651 dst: &mut CudaSlice<f32>,
16652 len_d: &CudaSlice<i32>,
16653 ncols: usize,
16654 ) -> Result<(), Box<dyn std::error::Error>> {
16655 let f = self.func("row_gather_dev_f32");
16656 let cfg = LaunchConfig::for_num_elems(ncols as u32);
16657 let nc = ncols as i32;
16658 let __s_b = self.gpu.stream();
16659 let mut b = __s_b.launch_builder(&f);
16660 b.arg(src).arg(dst).arg(len_d).arg(&nc);
16661 unsafe {
16662 b.launch(cfg)?;
16663 }
16664 Ok(())
16665 }
16666
16667 pub fn matmul_group(
16674 &self,
16675 ws: &[&crate::model::GpuTensor],
16676 x: &CudaSlice<f32>,
16677 m: usize,
16678 ) -> Result<Vec<CudaSlice<f32>>, Box<dyn std::error::Error>> {
16679 use crate::model::GpuTensor;
16680 let mut out = Vec::with_capacity(ws.len());
16681 let any_mirror = ws
16682 .iter()
16683 .any(|w| matches!(w, GpuTensor::Quant { f16: Some(_), .. }));
16684 if m >= 16 && any_mirror && !self.verify_exact_on() {
16685 let in_f = ws[0].in_features();
16686 let xh = self.f16_act(x, m * in_f, in_f)?;
16687 for w in ws {
16688 if w.in_features() == in_f {
16689 if let Some(y) = self.try_f16_gemm_pre(w, &xh, m)? {
16690 out.push(y);
16691 continue;
16692 }
16693 }
16694 out.push(self.matmul(w, x, m)?);
16695 }
16696 return Ok(out);
16697 }
16698 for w in ws {
16699 out.push(self.matmul(w, x, m)?);
16700 }
16701 Ok(out)
16702 }
16703
16704 pub fn matmul_group_multi(
16711 &self,
16712 ws: &[&crate::model::GpuTensor],
16713 xs: &[&CudaSlice<f32>],
16714 ms: &[usize],
16715 ) -> Result<Vec<Vec<CudaSlice<f32>>>, Box<dyn std::error::Error>> {
16716 assert_eq!(xs.len(), ms.len());
16717 let in_f = ws[0].in_features();
16718 let total: usize = ms.iter().sum();
16719 let mut xcat = self.uninit(total * in_f)?;
16720 let mut off = 0usize;
16721 for (x, &m) in xs.iter().zip(ms) {
16722 self.copy_into(&mut xcat, off * in_f, x, m * in_f)?;
16723 off += m;
16724 }
16725 let ys = self.matmul_group(ws, &xcat, total)?;
16726 let mut out: Vec<Vec<CudaSlice<f32>>> = (0..xs.len()).map(|_| Vec::new()).collect();
16727 for (w, y) in ws.iter().zip(ys) {
16728 let out_f = w.out_features();
16729 let mut off = 0usize;
16730 for (s, &m) in ms.iter().enumerate() {
16731 let mut ys_s = self.uninit(m * out_f)?;
16732 let src = y.slice(off * out_f..(off + m) * out_f);
16733 self.gpu.stream().memcpy_dtod(&src, &mut ys_s)?;
16734 out[s].push(ys_s);
16735 off += m;
16736 }
16737 }
16738 Ok(out)
16739 }
16740
16741 pub fn gemm_supports(&self, w: &crate::model::GpuTensor) -> bool {
16751 use crate::model::GpuTensor;
16752 if !legacy_quant_gemm_allowed(
16753 cfg!(memra_portable_cuda),
16754 cfg!(memra_hopper_mma),
16755 std::env::var_os("MEMRA_NO_GEMM").is_some(),
16756 ) {
16757 return false;
16758 }
16759 match w {
16760 GpuTensor::Quant { qtype, .. } => {
16761 matches!(*qtype, QT_Q8_0 | QT_Q4_K | QT_Q6_K | QT_Q5_K | QT_Q4_0)
16762 || (*qtype == QT_NVFP4 && w.in_features() % 64 == 0)
16763 }
16764 GpuTensor::Float { .. } | GpuTensor::FloatBf16 { .. } => false,
16765 }
16766 }
16767
16768 pub fn qmatvec_gemm(
16775 &self,
16776 w: &crate::model::GpuTensor,
16777 aq: &CudaSlice<i8>,
16778 ad: &CudaSlice<f32>,
16779 m: usize,
16780 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16781 use crate::model::GpuTensor;
16782 let in_f = w.in_features();
16783 let out_f = w.out_features();
16784 let (bytes, qtype, row_bytes, scale, rp) = match w {
16785 GpuTensor::Quant {
16786 bytes,
16787 qtype,
16788 row_bytes,
16789 scale,
16790 rp,
16791 ..
16792 } => (bytes, *qtype, *row_bytes, *scale, *rp),
16793 _ => unreachable!("gemm_supports guaranteed Quant"),
16794 };
16795 if cfg!(memra_hopper_mma) && qtype == QT_Q8_0 && out_f % 64 == 0 && wgmma_gemm_enabled() {
16801 if let GpuTensor::Quant { rp4: Some(m4), .. } = w {
16802 let mut y = self.qmatvec_gemm_q8_0_wgmma_raw(m4, aq, ad, m, in_f, out_f)?;
16803 if scale != 1.0 {
16804 self.scale_inplace(&mut y, scale, m * out_f)?;
16805 }
16806 return Ok(y);
16807 }
16808 }
16809 let name = match qtype {
16810 QT_Q8_0 => "qmatvec_gemm_q8_0",
16811 QT_Q4_K => "qmatvec_gemm_q4_K",
16812 QT_Q4_0 => {
16813 if rp {
16814 "qmatvec_gemm_q4_0_rp"
16815 } else {
16816 "qmatvec_gemm_q4_0"
16817 }
16818 }
16819 QT_Q5_K => "qmatvec_gemm_q5_K",
16820 QT_Q6_K => "qmatvec_gemm_q6_K",
16821 QT_NVFP4 => {
16822 if rp {
16823 "qmatvec_gemm_nvfp4_rp"
16824 } else {
16825 "qmatvec_gemm_nvfp4"
16826 }
16827 }
16828 _ => unreachable!(),
16829 };
16830 let f = self.func(name);
16831 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);
16836 let k1_tile = if is_k1 {
16838 k1_launch_override().unwrap_or((128, 128, 8))
16839 } else {
16840 (128, 128, 8)
16841 };
16842 let (bm, bn): (u32, u32) = if is_k1 {
16843 (k1_tile.0, k1_tile.1)
16844 } else {
16845 (64, 256)
16846 };
16847 let warps: u32 = if is_k1 {
16848 k1_tile.2
16849 } else {
16850 match qtype {
16851 QT_NVFP4 => 8,
16852 _ => 4,
16853 }
16854 };
16855 let cfg = LaunchConfig {
16856 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
16857 block_dim: (32, warps, 1),
16858 shared_mem_bytes: 0,
16859 };
16860 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
16861 let __s_b = self.gpu.stream();
16862 let mut b = __s_b.launch_builder(&f);
16863 b.arg(bytes)
16864 .arg(aq)
16865 .arg(ad)
16866 .arg(&mut y)
16867 .arg(&inf)
16868 .arg(&outf)
16869 .arg(&mi)
16870 .arg(&rb);
16871 unsafe {
16872 b.launch(cfg)?;
16873 }
16874 if scale != 1.0 {
16875 self.scale_inplace(&mut y, scale, m * out_f)?;
16876 }
16877 Ok(y)
16878 }
16879
16880 pub fn qmatvec_gemm_raw(
16885 &self,
16886 bytes: &CudaSlice<u8>,
16887 x: &CudaSlice<f32>,
16888 m: usize,
16889 in_f: usize,
16890 out_f: usize,
16891 qtype: i32,
16892 row_bytes: usize,
16893 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16894 let (aq, ad) = self.quantize_q8_1(x, m, in_f)?;
16895 let name = match qtype {
16896 QT_Q8_0 => "qmatvec_gemm_q8_0",
16897 QT_Q4_K => "qmatvec_gemm_q4_K",
16898 QT_Q4_0 => "qmatvec_gemm_q4_0",
16899 QT_Q5_K => "qmatvec_gemm_q5_K",
16900 QT_Q6_K => "qmatvec_gemm_q6_K",
16901 QT_NVFP4 => "qmatvec_gemm_nvfp4",
16902 QT_NVFP4_RP => "qmatvec_gemm_nvfp4_rp",
16903 _ => panic!("qmatvec_gemm_raw: qtype {qtype} has no GEMM kernel"),
16904 };
16905 let f = self.func(name);
16906 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);
16910 let k1_tile = if is_k1 {
16912 k1_launch_override().unwrap_or((128, 128, 8))
16913 } else {
16914 (128, 128, 8)
16915 };
16916 let (bm, bn): (u32, u32) = if is_k1 {
16917 (k1_tile.0, k1_tile.1)
16918 } else {
16919 (64, 256)
16920 };
16921 let warps: u32 = if is_k1 {
16922 k1_tile.2
16923 } else {
16924 match qtype {
16925 QT_NVFP4 | QT_NVFP4_RP => 8,
16926 _ => 4,
16927 }
16928 };
16929 let cfg = LaunchConfig {
16930 grid_dim: ((out_f as u32 + bm - 1) / bm, (m as u32 + bn - 1) / bn, 1),
16931 block_dim: (32, warps, 1),
16932 shared_mem_bytes: 0,
16933 };
16934 let (inf, outf, mi, rb) = (in_f as i32, out_f as i32, m as i32, row_bytes as i64);
16935 let __s_b = self.gpu.stream();
16936 let mut b = __s_b.launch_builder(&f);
16937 b.arg(bytes)
16938 .arg(&aq)
16939 .arg(&ad)
16940 .arg(&mut y)
16941 .arg(&inf)
16942 .arg(&outf)
16943 .arg(&mi)
16944 .arg(&rb);
16945 unsafe {
16946 b.launch(cfg)?;
16947 }
16948 Ok(y)
16949 }
16950
16951 pub fn qmatvec_gemm_q8_0_wgmma_raw(
16958 &self,
16959 rp4: &CudaSlice<u8>,
16960 aq: &CudaSlice<i8>,
16961 ad: &CudaSlice<f32>,
16962 m: usize,
16963 in_f: usize,
16964 out_f: usize,
16965 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
16966 assert!(
16967 out_f % 64 == 0 && in_f % 32 == 0,
16968 "wgmma GEMM needs out_f%64==0, in_f%32==0"
16969 );
16970 let f = self.func("qmatvec_gemm_q8_0_wgmma");
16971 let mut y = self.alloc_uninit::<f32>(m * out_f)?; let cfg = LaunchConfig {
16973 grid_dim: ((out_f / 64) as u32, (m as u32).div_ceil(64), 1),
16974 block_dim: (128, 1, 1),
16975 shared_mem_bytes: 0,
16976 };
16977 let (inf, outf, mi) = (in_f as i32, out_f as i32, m as i32);
16978 let __s_b = self.gpu.stream();
16979 let mut b = __s_b.launch_builder(&f);
16980 b.arg(rp4)
16981 .arg(aq)
16982 .arg(ad)
16983 .arg(&mut y)
16984 .arg(&inf)
16985 .arg(&outf)
16986 .arg(&mi);
16987 unsafe {
16988 b.launch(cfg)?;
16989 }
16990 Ok(y)
16991 }
16992
16993 pub fn scale_inplace(
16995 &self,
16996 y: &mut CudaSlice<f32>,
16997 s: f32,
16998 n: usize,
16999 ) -> Result<(), Box<dyn std::error::Error>> {
17000 let f = self.func("scale_f32");
17001 let cfg = LaunchConfig::for_num_elems(n as u32);
17002 let (sf, ni) = (s, n as i32);
17003 let __s_b = self.gpu.stream();
17004 let mut b = __s_b.launch_builder(&f);
17005 b.arg(y).arg(&sf).arg(&ni);
17006 unsafe {
17007 b.launch(cfg)?;
17008 }
17009 Ok(())
17010 }
17011
17012 pub fn bf16_to_f32(
17017 &self,
17018 data: &cudarc::driver::CudaView<'_, u8>,
17019 n: usize,
17020 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17021 let mut out = self.alloc_uninit::<f32>(n)?;
17022 let f = self.func("bf16_to_f32");
17023 let cfg = LaunchConfig::for_num_elems(n as u32);
17024 let ni = n as i32;
17025 let __s_b = self.gpu.stream();
17026 let mut b = __s_b.launch_builder(&f);
17027 b.arg(data).arg(&mut out).arg(&ni);
17028 unsafe {
17029 b.launch(cfg)?;
17030 }
17031 Ok(out)
17032 }
17033
17034 fn linear_bf16_chunked(
17041 &self,
17042 x: &CudaSlice<f32>,
17043 data: &CudaSlice<u8>,
17044 m: usize,
17045 in_f: usize,
17046 out_f: usize,
17047 exact: bool,
17048 canonical_chunk_rows: Option<usize>,
17049 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17050 static EXP_NS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
17054 static EXP_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
17055 static EXP_WBYTES: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
17056 let timing = std::env::var("MEMRA_STEP_TP_TIMING").as_deref() == Ok("1");
17057 let started = timing.then(std::time::Instant::now);
17058 let result =
17059 self.linear_bf16_chunked_inner(x, data, m, in_f, out_f, exact, canonical_chunk_rows);
17060 if let Some(started) = started {
17061 use std::sync::atomic::Ordering;
17062 self.stream().synchronize()?;
17063 let ns = EXP_NS.fetch_add(started.elapsed().as_nanos() as u64, Ordering::Relaxed)
17064 + started.elapsed().as_nanos() as u64;
17065 let wb = EXP_WBYTES.fetch_add((in_f * out_f * 2) as u64, Ordering::Relaxed)
17066 + (in_f * out_f * 2) as u64;
17067 let calls = EXP_CALLS.fetch_add(1, Ordering::Relaxed) + 1;
17068 if calls % 1024 == 0 {
17069 eprintln!(
17070 "[bf16-expand-timing] calls={calls} total_ms={:.1} avg_us={:.1} \
17071 weight_gb={:.2}",
17072 ns as f64 / 1.0e6,
17073 ns as f64 / calls as f64 / 1.0e3,
17074 wb as f64 / 1.0e9,
17075 );
17076 }
17077 }
17078 result
17079 }
17080
17081 pub(crate) fn bf16_mmv_on() -> bool {
17086 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17087 *ON.get_or_init(|| std::env::var("MEMRA_BF16_MMV").as_deref() == Ok("1"))
17088 }
17089
17090 fn matvec_bf16(
17093 &self,
17094 data: &CudaSlice<u8>,
17095 x: &CudaSlice<f32>,
17096 in_f: usize,
17097 out_f: usize,
17098 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
17099 if data.len() != in_f * out_f * 2 || x.len() < in_f || in_f % 8 != 0 {
17100 return Err(format!(
17101 "matvec_bf16 geometry bytes={} x={} in={in_f} out={out_f}",
17102 data.len(),
17103 x.len()
17104 )
17105 .into());
17106 }
17107 let mut y = self.alloc_uninit::<f32>(out_f)?;
17108 let f = self.func("matvec_bf16_f32acc");
17109 let cfg = LaunchConfig {
17110 grid_dim: (out_f as u32, 1, 1),
17111 block_dim: (mmv_block(), 1, 1),
17112 shared_mem_bytes: 0,
17113 };
17114 let ini = in_f as i32;
17115 let __s_bld = self.gpu.stream();
17116 let mut bld = __s_bld.launch_builder(&f);
17117 bld.arg(data).arg(x).arg(&mut y).arg(&ini);
17118 unsafe {
17119 bld.launch(cfg)?;
17120 }
17121 Ok(y)
17122 }
17123
17124 #[allow(clippy::too_many_arguments)]
17128 #[allow(clippy::too_many_arguments)]
17133 pub fn qk_norm_rope_append_inc_dcw(
17134 &self,
17135 q_raw: &CudaSlice<f32>,
17136 k_raw: &CudaSlice<f32>,
17137 v_raw: &CudaSlice<f32>,
17138 qw: &CudaSlice<f32>,
17139 kw: &CudaSlice<f32>,
17140 q_out: &mut CudaSlice<f32>,
17141 k_out: &mut CudaSlice<f32>,
17142 pos: &CudaSlice<i32>,
17143 k_plane: &mut CudaSlice<u8>,
17144 v_plane: &mut CudaSlice<u8>,
17145 len_dev: &CudaSlice<i32>,
17148 base_dev: Option<&CudaSlice<i32>>,
17149 done_ctr: &mut CudaSlice<u32>,
17150 kv_dim_k: usize,
17151 kv_dim_v: usize,
17152 k_tok_bytes: usize,
17153 v_tok_bytes: usize,
17154 head_dim: usize,
17155 n_dims: usize,
17156 nh_q: usize,
17157 nh_k: usize,
17158 eps: f32,
17159 freq_base: f32,
17160 freq_scale: f32,
17161 ff: Option<&CudaSlice<f32>>,
17162 ) -> Result<(), Box<dyn std::error::Error>> {
17163 if head_dim != 128
17164 || kv_dim_v != kv_dim_k
17165 || kv_dim_k != nh_k * head_dim
17166 || q_raw.len() < nh_q * head_dim
17167 || k_raw.len() < nh_k * head_dim
17168 || v_raw.len() < kv_dim_v
17169 || q_out.len() < nh_q * head_dim
17170 || k_out.len() < nh_k * head_dim
17171 || pos.is_empty()
17172 || done_ctr.is_empty()
17173 {
17174 return Err(format!(
17175 "qk_norm_rope_append_inc geometry head_dim={head_dim} nh_q={nh_q} nh_k={nh_k} kv_k={kv_dim_k} kv_v={kv_dim_v}"
17176 )
17177 .into());
17178 }
17179 let f = self.func("qk_norm_rope_append_inc_dcw");
17180 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
17181 let cfg = LaunchConfig {
17182 grid_dim: ((nh_q + nh_k) as u32, 1, 1),
17183 block_dim: (128, 1, 1),
17184 shared_mem_bytes: 0,
17185 };
17186 let (kvk, kvv) = (kv_dim_k as i32, kv_dim_v as i32);
17187 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
17188 let (hd, nd, nq) = (head_dim as i32, n_dims as i32, nh_q as i32);
17189 let null: u64 = 0;
17190 let __s_b = self.gpu.stream();
17191 let mut b = __s_b.launch_builder(&f);
17192 b.arg(q_raw)
17193 .arg(k_raw)
17194 .arg(v_raw)
17195 .arg(qw)
17196 .arg(kw)
17197 .arg(q_out)
17198 .arg(k_out)
17199 .arg(pos)
17200 .arg(&mut *k_plane)
17201 .arg(&mut *v_plane)
17202 .arg(len_dev);
17203 match base_dev {
17204 Some(base) => {
17205 b.arg(base);
17206 }
17207 None => {
17208 b.arg(&null);
17209 }
17210 }
17211 b.arg(&mut *done_ctr)
17212 .arg(&kvk)
17213 .arg(&kvv)
17214 .arg(&ktb)
17215 .arg(&vtb)
17216 .arg(&hd)
17217 .arg(&nd)
17218 .arg(&nq)
17219 .arg(&eps)
17220 .arg(&theta_scale)
17221 .arg(&freq_scale);
17222 match ff {
17223 Some(freqs) => {
17224 b.arg(freqs);
17225 }
17226 None => {
17227 b.arg(&null);
17228 }
17229 }
17230 unsafe {
17231 b.launch(cfg)?;
17232 }
17233 Ok(())
17234 }
17235
17236 pub fn qk_norm_rope_into(
17237 &self,
17238 q_raw: &CudaSlice<f32>,
17239 k_raw: &CudaSlice<f32>,
17240 qw: &CudaSlice<f32>,
17241 kw: &CudaSlice<f32>,
17242 q_out: &mut CudaSlice<f32>,
17243 k_out: &mut CudaSlice<f32>,
17244 pos: &CudaSlice<i32>,
17245 head_dim: usize,
17246 n_dims: usize,
17247 nh_q: usize,
17248 nh_k: usize,
17249 eps: f32,
17250 freq_base: f32,
17251 freq_scale: f32,
17252 ff: Option<&CudaSlice<f32>>,
17253 ) -> Result<(), Box<dyn std::error::Error>> {
17254 if head_dim > 512
17255 || q_raw.len() < nh_q * head_dim
17256 || k_raw.len() < nh_k * head_dim
17257 || q_out.len() < nh_q * head_dim
17258 || k_out.len() < nh_k * head_dim
17259 || qw.len() < head_dim
17260 || kw.len() < head_dim
17261 || pos.is_empty()
17262 {
17263 return Err(format!(
17264 "qk_norm_rope geometry head_dim={head_dim} nh_q={nh_q} nh_k={nh_k}"
17265 )
17266 .into());
17267 }
17268 let f = self.func("qk_norm_rope_f32");
17269 let theta_scale = (freq_base).powf(-2.0 / n_dims as f32);
17270 let cfg = LaunchConfig {
17271 grid_dim: ((nh_q + nh_k) as u32, 1, 1),
17272 block_dim: (128, 1, 1),
17273 shared_mem_bytes: 0,
17274 };
17275 let (hd, nd, nq) = (head_dim as i32, n_dims as i32, nh_q as i32);
17276 let __s_b = self.gpu.stream();
17277 let mut b = __s_b.launch_builder(&f);
17278 b.arg(q_raw)
17279 .arg(k_raw)
17280 .arg(qw)
17281 .arg(kw)
17282 .arg(q_out)
17283 .arg(k_out)
17284 .arg(pos)
17285 .arg(&hd)
17286 .arg(&nd)
17287 .arg(&nq)
17288 .arg(&eps)
17289 .arg(&theta_scale)
17290 .arg(&freq_scale);
17291 match ff {
17292 Some(ffv) => {
17293 b.arg(ffv);
17294 unsafe {
17295 b.launch(cfg)?;
17296 }
17297 }
17298 None => {
17299 let null: u64 = 0;
17300 b.arg(&null);
17301 unsafe {
17302 b.launch(cfg)?;
17303 }
17304 }
17305 }
17306 Ok(())
17307 }
17308
17309 #[allow(clippy::too_many_arguments)]
17312 pub fn matvec_f32_b4_into(
17313 &self,
17314 w: [&CudaSlice<f32>; 4],
17315 x: &CudaSlice<f32>,
17316 y: &mut CudaSlice<f32>,
17317 block_cols: usize,
17318 out_f: usize,
17319 ) -> Result<(), Box<dyn std::error::Error>> {
17320 if block_cols % 4 != 0
17321 || x.len() < 4 * block_cols
17322 || y.len() < out_f
17323 || w.iter().any(|w| w.len() != out_f * block_cols)
17324 {
17325 return Err(format!(
17326 "matvec_f32_b4 geometry block_cols={block_cols} out={out_f} x={}",
17327 x.len()
17328 )
17329 .into());
17330 }
17331 let f = self.func("matvec_f32_b4");
17332 let cfg = LaunchConfig {
17333 grid_dim: (out_f as u32, 1, 1),
17334 block_dim: (128, 1, 1),
17335 shared_mem_bytes: 0,
17336 };
17337 let (bc, of) = (block_cols as i32, out_f as i32);
17338 let __s_b = self.gpu.stream();
17339 let mut b = __s_b.launch_builder(&f);
17340 b.arg(w[0])
17341 .arg(w[1])
17342 .arg(w[2])
17343 .arg(w[3])
17344 .arg(x)
17345 .arg(y)
17346 .arg(&bc)
17347 .arg(&of);
17348 unsafe {
17349 b.launch(cfg)?;
17350 }
17351 Ok(())
17352 }
17353
17354 pub fn axpy_rows_seq_into(
17357 &self,
17358 x: &CudaSlice<f32>,
17359 w: &CudaSlice<f32>,
17360 y: &mut CudaSlice<f32>,
17361 width: usize,
17362 n_rows: usize,
17363 ) -> Result<(), Box<dyn std::error::Error>> {
17364 if x.len() < n_rows * width || w.len() < n_rows || y.len() < width {
17365 return Err(format!(
17366 "axpy_rows_seq geometry x={} w={} y={} width={width} rows={n_rows}",
17367 x.len(),
17368 w.len(),
17369 y.len()
17370 )
17371 .into());
17372 }
17373 let f = self.func("axpy_rows_seq_f32");
17374 let cfg = LaunchConfig::for_num_elems(width as u32);
17375 let (wi, nr) = (width as i32, n_rows as i32);
17376 let __s_b = self.gpu.stream();
17377 let mut b = __s_b.launch_builder(&f);
17378 b.arg(x).arg(w).arg(y).arg(&wi).arg(&nr);
17379 unsafe {
17380 b.launch(cfg)?;
17381 }
17382 Ok(())
17383 }
17384
17385 #[allow(clippy::too_many_arguments)]
17389 pub fn axpy_rows_seq_md_off_into(
17390 &self,
17391 x: &CudaSlice<f32>,
17392 w_route: &CudaSlice<f32>,
17393 md: &CudaSlice<f32>,
17394 sel: &CudaSlice<i32>,
17395 y: &mut CudaSlice<f32>,
17396 width: usize,
17397 n_rows: usize,
17398 row0: usize,
17399 ) -> Result<(), Box<dyn std::error::Error>> {
17400 if x.len() < (row0 + n_rows) * width
17401 || w_route.len() < row0 + n_rows
17402 || sel.len() < row0 + n_rows
17403 || y.len() < width
17404 {
17405 return Err(format!(
17406 "axpy_rows_seq_md_off geometry x={} w={} sel={} y={} width={width} \
17407 rows={n_rows} row0={row0}",
17408 x.len(),
17409 w_route.len(),
17410 sel.len(),
17411 y.len()
17412 )
17413 .into());
17414 }
17415 let f = self.func("axpy_rows_seq_md_off_f32");
17416 let cfg = LaunchConfig::for_num_elems(width as u32);
17417 let (wi, nr, r0) = (width as i32, n_rows as i32, row0 as i32);
17418 let __s_b = self.gpu.stream();
17419 let mut b = __s_b.launch_builder(&f);
17420 b.arg(x)
17421 .arg(w_route)
17422 .arg(md)
17423 .arg(sel)
17424 .arg(y)
17425 .arg(&wi)
17426 .arg(&nr)
17427 .arg(&r0);
17428 unsafe {
17429 b.launch(cfg)?;
17430 }
17431 Ok(())
17432 }
17433
17434 #[allow(clippy::too_many_arguments)]
17439 pub fn qmatvec_nvfp4_sel_gu_tcol_into(
17440 &self,
17441 gate_bank: &CudaSlice<u8>,
17442 up_bank: &CudaSlice<u8>,
17443 sel: &CudaSlice<i32>,
17444 aq: &CudaSlice<i8>,
17445 ad: &CudaSlice<f32>,
17446 yg: &mut CudaSlice<f32>,
17447 yu: &mut CudaSlice<f32>,
17448 n_sel: usize,
17449 n_sel_col: usize,
17450 in_f: usize,
17451 out_f: usize,
17452 row_bytes: usize,
17453 expert_stride: usize,
17454 act_row_stride: usize,
17455 ad_row_stride: usize,
17456 ) -> Result<(), Box<dyn std::error::Error>> {
17457 assert!(in_f % 64 == 0, "NVFP4 dp4a requires in_f % 64 == 0");
17458 if yg.len() < n_sel * out_f
17459 || yu.len() < n_sel * out_f
17460 || sel.len() < n_sel
17461 || n_sel_col == 0
17462 || n_sel % n_sel_col != 0
17463 {
17464 return Err("NVFP4 gu tcol geometry".into());
17465 }
17466 let f = self.func("qmatvec_nvfp4_dp4a_sel_v2_gu_tcol");
17467 let cfg = LaunchConfig {
17468 grid_dim: ((2 * out_f) as u32, n_sel as u32, 1),
17469 block_dim: (128, 1, 1),
17470 shared_mem_bytes: 0,
17471 };
17472 let (inf, outf, ns, nsc) = (in_f as i32, out_f as i32, n_sel as i32, n_sel_col as i32);
17473 let (rb, es) = (row_bytes as i64, expert_stride as i64);
17474 let (ars, adrs) = (act_row_stride as i64, ad_row_stride as i64);
17475 let __s_b = self.gpu.stream();
17476 let mut b = __s_b.launch_builder(&f);
17477 b.arg(gate_bank)
17478 .arg(up_bank)
17479 .arg(sel)
17480 .arg(aq)
17481 .arg(ad)
17482 .arg(yg)
17483 .arg(yu)
17484 .arg(&inf)
17485 .arg(&outf)
17486 .arg(&ns)
17487 .arg(&rb)
17488 .arg(&es)
17489 .arg(&ars)
17490 .arg(&adrs)
17491 .arg(&nsc);
17492 unsafe {
17493 b.launch(cfg)?;
17494 }
17495 Ok(())
17496 }
17497
17498 #[allow(clippy::too_many_arguments)]
17501 pub fn axpy_rows_seq_md_into(
17502 &self,
17503 x: &CudaSlice<f32>,
17504 w_route: &CudaSlice<f32>,
17505 md: &CudaSlice<f32>,
17506 sel: &CudaSlice<i32>,
17507 y: &mut CudaSlice<f32>,
17508 width: usize,
17509 n_rows: usize,
17510 ) -> Result<(), Box<dyn std::error::Error>> {
17511 if x.len() < n_rows * width
17512 || w_route.len() < n_rows
17513 || sel.len() < n_rows
17514 || y.len() < width
17515 {
17516 return Err(format!(
17517 "axpy_rows_seq_md geometry x={} w={} sel={} y={} width={width} rows={n_rows}",
17518 x.len(),
17519 w_route.len(),
17520 sel.len(),
17521 y.len()
17522 )
17523 .into());
17524 }
17525 let f = self.func("axpy_rows_seq_md_f32");
17526 let cfg = LaunchConfig::for_num_elems(width as u32);
17527 let (wi, nr) = (width as i32, n_rows as i32);
17528 let __s_b = self.gpu.stream();
17529 let mut b = __s_b.launch_builder(&f);
17530 b.arg(x)
17531 .arg(w_route)
17532 .arg(md)
17533 .arg(sel)
17534 .arg(y)
17535 .arg(&wi)
17536 .arg(&nr);
17537 unsafe {
17538 b.launch(cfg)?;
17539 }
17540 Ok(())
17541 }
17542
17543 #[allow(clippy::too_many_arguments)]
17545 #[allow(clippy::too_many_arguments)]
17549 pub fn matvec_bf16_qkvg_tcol_into(
17550 &self,
17551 wq: &CudaSlice<u8>,
17552 wk: &CudaSlice<u8>,
17553 wv: &CudaSlice<u8>,
17554 wg: &CudaSlice<u8>,
17555 x_t: &CudaSlice<f32>,
17556 yq: &mut CudaSlice<f32>,
17557 yk: &mut CudaSlice<f32>,
17558 yv: &mut CudaSlice<f32>,
17559 yg: &mut CudaSlice<f32>,
17560 in_f: usize,
17561 out_q: usize,
17562 out_kv: usize,
17563 out_g: usize,
17564 t: usize,
17565 ) -> Result<(), Box<dyn std::error::Error>> {
17566 if t == 0
17567 || t > 8
17568 || in_f % 8 != 0
17569 || x_t.len() < t * in_f
17570 || yq.len() < t * out_q
17571 || yk.len() < t * out_kv
17572 || yv.len() < t * out_kv
17573 || (out_g > 0 && yg.len() < t * out_g)
17574 {
17575 return Err("matvec_bf16_qkvg_tcol geometry".into());
17576 }
17577 let f = self.func("matvec_bf16_qkvg_tcol");
17578 let grid = out_q + 2 * out_kv + out_g;
17579 let cfg = LaunchConfig {
17580 grid_dim: (grid as u32, 1, 1),
17581 block_dim: (mmv_block(), 1, 1),
17582 shared_mem_bytes: 0,
17583 };
17584 let (ini, oq, okv, og, ti) = (
17585 in_f as i32,
17586 out_q as i32,
17587 out_kv as i32,
17588 out_g as i32,
17589 t as i32,
17590 );
17591 let __s_b = self.gpu.stream();
17592 let mut b = __s_b.launch_builder(&f);
17593 b.arg(wq)
17594 .arg(wk)
17595 .arg(wv)
17596 .arg(wg)
17597 .arg(x_t)
17598 .arg(yq)
17599 .arg(yk)
17600 .arg(yv)
17601 .arg(yg)
17602 .arg(&ini)
17603 .arg(&oq)
17604 .arg(&okv)
17605 .arg(&og)
17606 .arg(&ti);
17607 unsafe {
17608 b.launch(cfg)?;
17609 }
17610 Ok(())
17611 }
17612
17613 pub fn matvec_bf16_qkvg_into(
17614 &self,
17615 wq: &CudaSlice<u8>,
17616 wk: &CudaSlice<u8>,
17617 wv: &CudaSlice<u8>,
17618 wg: &CudaSlice<u8>,
17619 x: &CudaSlice<f32>,
17620 yq: &mut CudaSlice<f32>,
17621 yk: &mut CudaSlice<f32>,
17622 yv: &mut CudaSlice<f32>,
17623 yg: &mut CudaSlice<f32>,
17624 in_f: usize,
17625 out_q: usize,
17626 out_kv: usize,
17627 out_g: usize,
17628 ) -> Result<(), Box<dyn std::error::Error>> {
17629 if in_f % 8 != 0
17630 || wq.len() != out_q * in_f * 2
17631 || wk.len() != out_kv * in_f * 2
17632 || wv.len() != out_kv * in_f * 2
17633 || wg.len() < out_g * in_f * 2
17634 || x.len() < in_f
17635 || yq.len() < out_q
17636 || yk.len() < out_kv
17637 || yv.len() < out_kv
17638 || (out_g > 0 && yg.len() < out_g)
17639 {
17640 return Err(format!(
17641 "fused bf16 QKV geometry in={in_f} out_q={out_q} out_kv={out_kv} out_g={out_g}"
17642 )
17643 .into());
17644 }
17645 let f = self.func("matvec_bf16_qkvg");
17646 let cfg = LaunchConfig {
17647 grid_dim: ((out_q + 2 * out_kv + out_g) as u32, 1, 1),
17648 block_dim: (mmv_block(), 1, 1),
17649 shared_mem_bytes: 0,
17650 };
17651 let (inf, oq, okv, og) = (in_f as i32, out_q as i32, out_kv as i32, out_g as i32);
17652 let __s_b = self.gpu.stream();
17653 let mut b = __s_b.launch_builder(&f);
17654 b.arg(wq)
17655 .arg(wk)
17656 .arg(wv)
17657 .arg(wg)
17658 .arg(x)
17659 .arg(yq)
17660 .arg(yk)
17661 .arg(yv)
17662 .arg(yg)
17663 .arg(&inf)
17664 .arg(&oq)
17665 .arg(&okv)
17666 .arg(&og);
17667 unsafe {
17668 b.launch(cfg)?;
17669 }
17670 Ok(())
17671 }
17672
17673 pub fn matvec_bf16_b4_into(
17675 &self,
17676 w: [&CudaSlice<u8>; 4],
17677 x: &CudaSlice<f32>,
17678 y: &mut CudaSlice<f32>,
17679 block_cols: usize,
17680 out_f: usize,
17681 ) -> Result<(), Box<dyn std::error::Error>> {
17682 if block_cols % 8 != 0
17683 || x.len() < 4 * block_cols
17684 || y.len() < out_f
17685 || w.iter().any(|w| w.len() != out_f * block_cols * 2)
17686 {
17687 return Err(format!(
17688 "bf16 b4 geometry block_cols={block_cols} out={out_f} x={}",
17689 x.len()
17690 )
17691 .into());
17692 }
17693 static B4_X2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17696 let x2 = *B4_X2.get_or_init(|| std::env::var("MEMRA_B4_X2").as_deref() == Ok("1"));
17697 let f = self.func(if x2 {
17698 "matvec_bf16_b4_x2"
17699 } else {
17700 "matvec_bf16_b4"
17701 });
17702 let grid = if x2 { out_f.div_ceil(2) } else { out_f };
17703 let cfg = LaunchConfig {
17704 grid_dim: (grid as u32, 1, 1),
17705 block_dim: (mmv_block(), 1, 1),
17706 shared_mem_bytes: 0,
17707 };
17708 let (bc, of) = (block_cols as i32, out_f as i32);
17709 let __s_b = self.gpu.stream();
17710 let mut b = __s_b.launch_builder(&f);
17711 b.arg(w[0])
17712 .arg(w[1])
17713 .arg(w[2])
17714 .arg(w[3])
17715 .arg(x)
17716 .arg(y)
17717 .arg(&bc)
17718 .arg(&of);
17719 unsafe {
17720 b.launch(cfg)?;
17721 }
17722 Ok(())
17723 }
17724
17725 pub fn matvec_bf16_b4_tcol_into(
17731 &self,
17732 w: [&CudaSlice<u8>; 4],
17733 x_t: &CudaSlice<f32>,
17734 y_t: &mut CudaSlice<f32>,
17735 block_cols: usize,
17736 out_f: usize,
17737 t: usize,
17738 ) -> Result<(), Box<dyn std::error::Error>> {
17739 if block_cols % 8 != 0
17740 || t == 0
17741 || t > 8
17742 || x_t.len() < t * 4 * block_cols
17743 || y_t.len() < t * out_f
17744 || w.iter().any(|w| w.len() != out_f * block_cols * 2)
17745 {
17746 return Err(format!(
17747 "bf16 b4 tcol geometry block_cols={block_cols} out={out_f} t={t} x={}",
17748 x_t.len()
17749 )
17750 .into());
17751 }
17752 if std::env::var("MEMRA_B4_X2").as_deref() == Ok("1") {
17753 return Err(
17754 "b4 tcol verify is qualified against the plain b4 kernel only \
17755 (MEMRA_B4_X2=1 is a different t=1 program)"
17756 .into(),
17757 );
17758 }
17759 let f = self.func("matvec_bf16_b4_tcol");
17760 let cfg = LaunchConfig {
17761 grid_dim: (out_f as u32, 1, 1),
17762 block_dim: (mmv_block(), 1, 1),
17763 shared_mem_bytes: 0,
17764 };
17765 let (bc, of, ti) = (block_cols as i32, out_f as i32, t as i32);
17766 let __s_b = self.gpu.stream();
17767 let mut b = __s_b.launch_builder(&f);
17768 b.arg(w[0])
17769 .arg(w[1])
17770 .arg(w[2])
17771 .arg(w[3])
17772 .arg(x_t)
17773 .arg(y_t)
17774 .arg(&bc)
17775 .arg(&of)
17776 .arg(&ti);
17777 unsafe {
17778 b.launch(cfg)?;
17779 }
17780 Ok(())
17781 }
17782
17783 pub fn matvec_bf16_into(
17785 &self,
17786 data: &CudaSlice<u8>,
17787 x: &CudaSlice<f32>,
17788 y: &mut CudaSlice<f32>,
17789 in_f: usize,
17790 out_f: usize,
17791 ) -> Result<(), Box<dyn std::error::Error>> {
17792 if data.len() != in_f * out_f * 2 || x.len() < in_f || in_f % 8 != 0 || y.len() < out_f {
17793 return Err(format!(
17794 "matvec_bf16_into geometry bytes={} x={} y={} in={in_f} out={out_f}",
17795 data.len(),
17796 x.len(),
17797 y.len()
17798 )
17799 .into());
17800 }
17801 static X4: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
17805 let x4 = *X4.get_or_init(|| std::env::var("MEMRA_DOWN_X4").as_deref() == Ok("1"))
17806 && in_f <= 2048;
17807 if x4 {
17808 let f = self.func("matvec_bf16_f32acc_x4");
17809 let cfg = LaunchConfig {
17810 grid_dim: (out_f.div_ceil(4) as u32, 1, 1),
17811 block_dim: (mmv_block(), 1, 1),
17812 shared_mem_bytes: 0,
17813 };
17814 let (ini, outi) = (in_f as i32, out_f as i32);
17815 let __s_b = self.gpu.stream();
17816 let mut b = __s_b.launch_builder(&f);
17817 b.arg(data).arg(x).arg(y).arg(&ini).arg(&outi);
17818 unsafe {
17819 b.launch(cfg)?;
17820 }
17821 return Ok(());
17822 }
17823 let f = self.func("matvec_bf16_f32acc");
17824 let cfg = LaunchConfig {
17825 grid_dim: (out_f as u32, 1, 1),
17826 block_dim: (mmv_block(), 1, 1),
17827 shared_mem_bytes: 0,
17828 };
17829 let ini = in_f as i32;
17830 let __s_b = self.gpu.stream();
17831 let mut b = __s_b.launch_builder(&f);
17832 b.arg(data).arg(x).arg(y).arg(&ini);
17833 unsafe {
17834 b.launch(cfg)?;
17835 }
17836 Ok(())
17837 }
17838
17839 pub fn matvec_bf16_view_into(
17842 &self,
17843 data: &cudarc::driver::CudaView<'_, u8>,
17844 x: &CudaSlice<f32>,
17845 y: &mut CudaSlice<f32>,
17846 in_f: usize,
17847 out_f: usize,
17848 ) -> Result<(), Box<dyn std::error::Error>> {
17849 if data.len() != in_f * out_f * 2 || x.len() < in_f || in_f % 8 != 0 || y.len() < out_f {
17850 return Err(format!(
17851 "matvec_bf16_view_into geometry bytes={} x={} y={} in={in_f} out={out_f}",
17852 data.len(),
17853 x.len(),
17854 y.len()
17855 )
17856 .into());
17857 }
17858 let f = self.func("matvec_bf16_f32acc");
17859 let cfg = LaunchConfig {
17860 grid_dim: (out_f as u32, 1, 1),
17861 block_dim: (mmv_block(), 1, 1),
17862 shared_mem_bytes: 0,
17863 };
17864 let ini = in_f as i32;
17865 let __s_b = self.gpu.stream();
17866 let mut b = __s_b.launch_builder(&f);
17867 b.arg(data).arg(x).arg(y).arg(&ini);
17868 unsafe {
17869 b.launch(cfg)?;
17870 }
17871 Ok(())
17872 }
17873
17874 pub fn matvec_bf16_raw_out(
17877 &self,
17878 w: &CudaSlice<u8>,
17879 x: &CudaSlice<f32>,
17880 y_raw: u64,
17881 in_f: usize,
17882 out_f: usize,
17883 ) -> Result<(), Box<dyn std::error::Error>> {
17884 if w.len() != in_f * out_f * 2 || x.len() < in_f || in_f % 8 != 0 || y_raw == 0 {
17885 return Err("matvec_bf16_raw_out geometry".into());
17886 }
17887 let f = self.func("matvec_bf16_f32acc");
17888 let cfg = LaunchConfig {
17889 grid_dim: (out_f as u32, 1, 1),
17890 block_dim: (mmv_block(), 1, 1),
17891 shared_mem_bytes: 0,
17892 };
17893 let ini = in_f as i32;
17894 let __s_b = self.gpu.stream();
17895 let mut b = __s_b.launch_builder(&f);
17896 b.arg(w).arg(x).arg(&y_raw).arg(&ini);
17897 unsafe {
17898 b.launch(cfg)?;
17899 }
17900 Ok(())
17901 }
17902
17903 pub fn add3_raw(
17907 &self,
17908 a: &CudaSlice<f32>,
17909 b: &CudaSlice<f32>,
17910 sh_raw: u64,
17911 scale_raw: u64,
17912 dst: &mut CudaSlice<f32>,
17913 n: usize,
17914 ) -> Result<(), Box<dyn std::error::Error>> {
17915 if a.len() < n || b.len() < n || dst.len() < n || sh_raw == 0 || scale_raw == 0 {
17916 return Err("add3_raw geometry".into());
17917 }
17918 let f = self.func("add3_f32");
17919 let cfg = LaunchConfig {
17920 grid_dim: ((n as u32).div_ceil(256), 1, 1),
17921 block_dim: (256, 1, 1),
17922 shared_mem_bytes: 0,
17923 };
17924 let ni = n as i32;
17925 let __s_b = self.gpu.stream();
17926 let mut bld = __s_b.launch_builder(&f);
17927 bld.arg(a)
17928 .arg(b)
17929 .arg(&sh_raw)
17930 .arg(&scale_raw)
17931 .arg(dst)
17932 .arg(&ni);
17933 unsafe {
17934 bld.launch(cfg)?;
17935 }
17936 Ok(())
17937 }
17938
17939 pub fn matvec_bf16_down_addscale_into(
17942 &self,
17943 w: &CudaSlice<u8>,
17944 x: &CudaSlice<f32>,
17945 scale: &CudaSlice<f32>,
17946 dst: &mut CudaSlice<f32>,
17947 in_f: usize,
17948 out_f: usize,
17949 ) -> Result<(), Box<dyn std::error::Error>> {
17950 if w.len() != in_f * out_f * 2
17951 || x.len() < in_f
17952 || in_f % 8 != 0
17953 || dst.len() < out_f
17954 || scale.is_empty()
17955 {
17956 return Err("matvec_bf16_down_addscale geometry".into());
17957 }
17958 let f = self.func("matvec_bf16_down_addscale");
17959 let cfg = LaunchConfig {
17960 grid_dim: (out_f as u32, 1, 1),
17961 block_dim: (mmv_block(), 1, 1),
17962 shared_mem_bytes: 0,
17963 };
17964 let ini = in_f as i32;
17965 let __s_b = self.gpu.stream();
17966 let mut b = __s_b.launch_builder(&f);
17967 b.arg(w).arg(x).arg(scale).arg(dst).arg(&ini);
17968 unsafe {
17969 b.launch(cfg)?;
17970 }
17971 Ok(())
17972 }
17973
17974 pub fn matvec_bf16_dual_silu_into(
17977 &self,
17978 wg: &CudaSlice<u8>,
17979 wu: &CudaSlice<u8>,
17980 x: &CudaSlice<f32>,
17981 act: &mut CudaSlice<f32>,
17982 in_f: usize,
17983 out_f: usize,
17984 limit: Option<f32>,
17985 ) -> Result<(), Box<dyn std::error::Error>> {
17986 if wg.len() != in_f * out_f * 2
17987 || wu.len() != in_f * out_f * 2
17988 || x.len() < in_f
17989 || in_f % 8 != 0
17990 || act.len() < out_f
17991 {
17992 return Err("matvec_bf16_dual_silu geometry".into());
17993 }
17994 let f = self.func("matvec_bf16_dual_silu");
17995 let cfg = LaunchConfig {
17996 grid_dim: (out_f as u32, 1, 1),
17997 block_dim: (mmv_block(), 1, 1),
17998 shared_mem_bytes: 0,
17999 };
18000 let (ini, outi) = (in_f as i32, out_f as i32);
18001 let lim = limit.unwrap_or(0.0);
18002 let __s_b = self.gpu.stream();
18003 let mut b = __s_b.launch_builder(&f);
18004 b.arg(wg)
18005 .arg(wu)
18006 .arg(x)
18007 .arg(act)
18008 .arg(&ini)
18009 .arg(&outi)
18010 .arg(&lim);
18011 unsafe {
18012 b.launch(cfg)?;
18013 }
18014 Ok(())
18015 }
18016
18017 #[allow(clippy::too_many_arguments)]
18020 pub fn matvec_bf16_dual_view_into(
18021 &self,
18022 wg: &cudarc::driver::CudaView<'_, u8>,
18023 wu: &cudarc::driver::CudaView<'_, u8>,
18024 x: &CudaSlice<f32>,
18025 yg: &mut CudaSlice<f32>,
18026 yu: &mut CudaSlice<f32>,
18027 in_f: usize,
18028 out_f: usize,
18029 ) -> Result<(), Box<dyn std::error::Error>> {
18030 if wg.len() != in_f * out_f * 2
18031 || wu.len() != in_f * out_f * 2
18032 || x.len() < in_f
18033 || in_f % 8 != 0
18034 || yg.len() < out_f
18035 || yu.len() < out_f
18036 {
18037 return Err(format!(
18038 "matvec_bf16_dual_view_into geometry wg={} wu={} x={} in={in_f} out={out_f}",
18039 wg.len(),
18040 wu.len(),
18041 x.len()
18042 )
18043 .into());
18044 }
18045 let f = self.func("matvec_bf16_dual");
18046 let cfg = LaunchConfig {
18047 grid_dim: ((2 * out_f) as u32, 1, 1),
18048 block_dim: (mmv_block(), 1, 1),
18049 shared_mem_bytes: 0,
18050 };
18051 let (ini, outi) = (in_f as i32, out_f as i32);
18052 let __s_b = self.gpu.stream();
18053 let mut b = __s_b.launch_builder(&f);
18054 b.arg(wg)
18055 .arg(wu)
18056 .arg(x)
18057 .arg(yg)
18058 .arg(yu)
18059 .arg(&ini)
18060 .arg(&outi);
18061 unsafe {
18062 b.launch(cfg)?;
18063 }
18064 Ok(())
18065 }
18066
18067 #[allow(clippy::too_many_arguments)]
18069 pub fn matvec_bf16_dual_into(
18070 &self,
18071 wg: &CudaSlice<u8>,
18072 wu: &CudaSlice<u8>,
18073 x: &CudaSlice<f32>,
18074 yg: &mut CudaSlice<f32>,
18075 yu: &mut CudaSlice<f32>,
18076 in_f: usize,
18077 out_f: usize,
18078 ) -> Result<(), Box<dyn std::error::Error>> {
18079 if wg.len() != in_f * out_f * 2
18080 || wu.len() != in_f * out_f * 2
18081 || x.len() < in_f
18082 || in_f % 8 != 0
18083 || yg.len() < out_f
18084 || yu.len() < out_f
18085 {
18086 return Err(format!(
18087 "matvec_bf16_dual_into geometry wg={} wu={} x={} in={in_f} out={out_f}",
18088 wg.len(),
18089 wu.len(),
18090 x.len()
18091 )
18092 .into());
18093 }
18094 let f = self.func("matvec_bf16_dual");
18095 let cfg = LaunchConfig {
18096 grid_dim: ((2 * out_f) as u32, 1, 1),
18097 block_dim: (mmv_block(), 1, 1),
18098 shared_mem_bytes: 0,
18099 };
18100 let (ini, outi) = (in_f as i32, out_f as i32);
18101 let __s_b = self.gpu.stream();
18102 let mut b = __s_b.launch_builder(&f);
18103 b.arg(wg)
18104 .arg(wu)
18105 .arg(x)
18106 .arg(yg)
18107 .arg(yu)
18108 .arg(&ini)
18109 .arg(&outi);
18110 unsafe {
18111 b.launch(cfg)?;
18112 }
18113 Ok(())
18114 }
18115
18116 pub(crate) fn matvec_bf16_dual(
18119 &self,
18120 wg: &CudaSlice<u8>,
18121 wu: &CudaSlice<u8>,
18122 x: &CudaSlice<f32>,
18123 in_f: usize,
18124 out_f: usize,
18125 ) -> Result<(CudaSlice<f32>, CudaSlice<f32>), Box<dyn std::error::Error>> {
18126 if wg.len() != in_f * out_f * 2
18127 || wu.len() != in_f * out_f * 2
18128 || x.len() < in_f
18129 || in_f % 8 != 0
18130 {
18131 return Err(format!(
18132 "matvec_bf16_dual geometry wg={} wu={} x={} in={in_f} out={out_f}",
18133 wg.len(),
18134 wu.len(),
18135 x.len()
18136 )
18137 .into());
18138 }
18139 let mut yg = self.alloc_uninit::<f32>(out_f)?;
18140 let mut yu = self.alloc_uninit::<f32>(out_f)?;
18141 let f = self.func("matvec_bf16_dual");
18142 let cfg = LaunchConfig {
18143 grid_dim: ((2 * out_f) as u32, 1, 1),
18144 block_dim: (mmv_block(), 1, 1),
18145 shared_mem_bytes: 0,
18146 };
18147 let (ini, outi) = (in_f as i32, out_f as i32);
18148 let __s_b = self.gpu.stream();
18149 let mut b = __s_b.launch_builder(&f);
18150 b.arg(wg)
18151 .arg(wu)
18152 .arg(x)
18153 .arg(&mut yg)
18154 .arg(&mut yu)
18155 .arg(&ini)
18156 .arg(&outi);
18157 unsafe {
18158 b.launch(cfg)?;
18159 }
18160 Ok((yg, yu))
18161 }
18162
18163 #[allow(clippy::too_many_arguments)]
18164 fn linear_bf16_chunked_inner(
18165 &self,
18166 x: &CudaSlice<f32>,
18167 data: &CudaSlice<u8>,
18168 m: usize,
18169 in_f: usize,
18170 out_f: usize,
18171 exact: bool,
18172 canonical_chunk_rows: Option<usize>,
18173 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18174 const CHUNK_BYTES: usize = 256 << 20;
18175 if m == 1
18178 && !exact
18179 && canonical_chunk_rows.is_none()
18180 && in_f % 8 == 0
18181 && Self::bf16_mmv_on()
18182 {
18183 return self.matvec_bf16(data, x, in_f, out_f);
18184 }
18185 let row_bytes = in_f
18186 .checked_mul(std::mem::size_of::<f32>())
18187 .ok_or("BF16 chunk row byte count overflow")?;
18188 if row_bytes == 0 || out_f == 0 {
18189 return Err("BF16 chunk dimensions must be nonzero".into());
18190 }
18191 let max_chunk_rows = (CHUNK_BYTES / row_bytes).max(1).min(out_f);
18192 let chunk_rows = match canonical_chunk_rows {
18193 Some(rows) if rows == 0 => {
18194 return Err("canonical BF16 chunk rows must be nonzero".into());
18195 }
18196 Some(rows) if rows > max_chunk_rows => {
18197 return Err(format!(
18198 "canonical BF16 chunk rows {rows} exceed the {max_chunk_rows}-row scratch limit"
18199 )
18200 .into());
18201 }
18202 Some(rows) if out_f % rows != 0 => {
18203 return Err(format!(
18204 "BF16 output width {out_f} is not divisible by canonical {rows}-row chunks"
18205 )
18206 .into());
18207 }
18208 Some(rows) => rows,
18209 None => max_chunk_rows,
18210 };
18211 if chunk_rows >= out_f {
18212 let wf32 = self.bf16_to_f32(&data.slice(0..in_f * out_f * 2), in_f * out_f)?;
18213 return if exact {
18214 self.linear_decode_exact(x, &wf32, m, in_f, out_f)
18215 } else {
18216 self.linear(x, &wf32, m, in_f, out_f)
18217 };
18218 }
18219 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
18220 let mut r0 = 0usize;
18221 while r0 < out_f {
18222 let rows = chunk_rows.min(out_f - r0);
18223 let wslice = data.slice(r0 * in_f * 2..(r0 + rows) * in_f * 2);
18224 let wf32 = self.bf16_to_f32(&wslice, in_f * rows)?;
18225 let yc = if exact {
18226 self.linear_decode_exact(x, &wf32, m, in_f, rows)?
18227 } else {
18228 self.linear(x, &wf32, m, in_f, rows)?
18229 };
18230 for mi in 0..m {
18232 let src = yc.slice(mi * rows..(mi + 1) * rows);
18233 let mut dst = y.slice_mut(mi * out_f + r0..mi * out_f + r0 + rows);
18234 self.gpu.stream().memcpy_dtod(&src, &mut dst)?;
18235 }
18236 r0 += rows;
18237 }
18238 Ok(y)
18239 }
18240
18241 pub fn linear_bf16_resident(
18245 &self,
18246 x: &CudaSlice<f32>,
18247 data: &CudaSlice<u8>,
18248 m: usize,
18249 in_f: usize,
18250 out_f: usize,
18251 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18252 if data.len() != in_f * out_f * 2 {
18253 return Err(format!("resident BF16 bytes {} != {out_f}x{in_f}x2", data.len()).into());
18254 }
18255 self.linear_bf16_chunked(x, data, m, in_f, out_f, false, None)
18256 }
18257
18258 pub fn linear_bf16_resident_canonical_rows(
18264 &self,
18265 x: &CudaSlice<f32>,
18266 data: &CudaSlice<u8>,
18267 m: usize,
18268 in_f: usize,
18269 out_f: usize,
18270 canonical_chunk_rows: usize,
18271 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18272 if data.len() != in_f * out_f * 2 {
18273 return Err(format!("resident BF16 bytes {} != {out_f}x{in_f}x2", data.len()).into());
18274 }
18275 self.linear_bf16_chunked(x, data, m, in_f, out_f, false, Some(canonical_chunk_rows))
18276 }
18277
18278 pub fn linear_f32_resident_canonical_rows(
18283 &self,
18284 x: &CudaSlice<f32>,
18285 data: &CudaSlice<f32>,
18286 m: usize,
18287 in_f: usize,
18288 out_f: usize,
18289 canonical_chunk_rows: usize,
18290 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18291 self.linear_f32_resident_canonical_rows_inner(
18292 x,
18293 data,
18294 m,
18295 in_f,
18296 out_f,
18297 canonical_chunk_rows,
18298 false,
18299 )
18300 }
18301
18302 pub fn linear_f32_resident_canonical_rows_strided(
18308 &self,
18309 x: &CudaSlice<f32>,
18310 data: &CudaSlice<f32>,
18311 m: usize,
18312 in_f: usize,
18313 out_f: usize,
18314 canonical_chunk_rows: usize,
18315 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18316 self.linear_f32_resident_canonical_rows_inner(
18317 x,
18318 data,
18319 m,
18320 in_f,
18321 out_f,
18322 canonical_chunk_rows,
18323 true,
18324 )
18325 }
18326
18327 fn linear_f32_resident_canonical_rows_inner(
18328 &self,
18329 x: &CudaSlice<f32>,
18330 data: &CudaSlice<f32>,
18331 m: usize,
18332 in_f: usize,
18333 out_f: usize,
18334 canonical_chunk_rows: usize,
18335 strided_output: bool,
18336 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18337 if data.len() != in_f * out_f {
18338 return Err(format!("resident F32 values {} != {out_f}x{in_f}", data.len()).into());
18339 }
18340 if canonical_chunk_rows == 0
18341 || canonical_chunk_rows > out_f
18342 || out_f % canonical_chunk_rows != 0
18343 {
18344 return Err(format!(
18345 "invalid canonical F32 chunk rows {canonical_chunk_rows} for output width {out_f}"
18346 )
18347 .into());
18348 }
18349 if canonical_chunk_rows == out_f {
18350 return self.linear(x, data, m, in_f, out_f);
18351 }
18352
18353 let mut y = self.alloc_uninit::<f32>(m * out_f)?;
18354 let input = x.slice(0..x.len());
18355 for r0 in (0..out_f).step_by(canonical_chunk_rows) {
18356 let weights = data.slice(r0 * in_f..(r0 + canonical_chunk_rows) * in_f);
18357 if m == 1 {
18358 let mut destination = y.slice_mut(r0..r0 + canonical_chunk_rows);
18359 self.linear_device_into(
18360 &input,
18361 &weights,
18362 &mut destination,
18363 1,
18364 in_f,
18365 canonical_chunk_rows,
18366 )?;
18367 continue;
18368 }
18369 let chunk = self.linear_device(&input, &weights, m, in_f, canonical_chunk_rows)?;
18370 if strided_output {
18371 self.place_rows_strided(&chunk, &mut y, canonical_chunk_rows, m, out_f, r0)?;
18372 } else {
18373 for token in 0..m {
18374 let source = chunk
18375 .slice(token * canonical_chunk_rows..(token + 1) * canonical_chunk_rows);
18376 let mut destination =
18377 y.slice_mut(token * out_f + r0..token * out_f + r0 + canonical_chunk_rows);
18378 self.gpu.stream().memcpy_dtod(&source, &mut destination)?;
18379 }
18380 }
18381 }
18382 Ok(y)
18383 }
18384
18385 pub fn linear_f32_resident_canonical_rows_t1_into(
18390 &self,
18391 x: &CudaSlice<f32>,
18392 data: &CudaSlice<f32>,
18393 y: &mut CudaSlice<f32>,
18394 in_f: usize,
18395 out_f: usize,
18396 canonical_chunk_rows: usize,
18397 ) -> Result<(), Box<dyn std::error::Error>> {
18398 if data.len() != in_f * out_f {
18399 return Err(format!("resident F32 values {} != {out_f}x{in_f}", data.len()).into());
18400 }
18401 if y.len() != out_f || x.len() != in_f {
18402 return Err(format!(
18403 "resident F32 t1 shapes x={} y={} != in {in_f} out {out_f}",
18404 x.len(),
18405 y.len()
18406 )
18407 .into());
18408 }
18409 if canonical_chunk_rows == 0
18410 || canonical_chunk_rows > out_f
18411 || out_f % canonical_chunk_rows != 0
18412 {
18413 return Err(format!(
18414 "invalid canonical F32 chunk rows {canonical_chunk_rows} for output width {out_f}"
18415 )
18416 .into());
18417 }
18418 let input = x.slice(0..x.len());
18419 for r0 in (0..out_f).step_by(canonical_chunk_rows) {
18420 let weights = data.slice(r0 * in_f..(r0 + canonical_chunk_rows) * in_f);
18421 let mut destination = y.slice_mut(r0..r0 + canonical_chunk_rows);
18422 self.linear_device_into(
18423 &input,
18424 &weights,
18425 &mut destination,
18426 1,
18427 in_f,
18428 canonical_chunk_rows,
18429 )?;
18430 }
18431 Ok(())
18432 }
18433
18434 pub fn linear_t1_into(
18437 &self,
18438 x: &cudarc::driver::CudaView<'_, f32>,
18439 w: &cudarc::driver::CudaView<'_, f32>,
18440 y: &mut cudarc::driver::CudaViewMut<'_, f32>,
18441 in_f: usize,
18442 out_f: usize,
18443 ) -> Result<(), Box<dyn std::error::Error>> {
18444 self.linear_device_into(x, w, y, 1, in_f, out_f)
18445 }
18446
18447 pub fn linear_decode_exact(
18454 &self,
18455 x: &CudaSlice<f32>,
18456 w: &CudaSlice<f32>,
18457 m_tokens: usize,
18458 in_f: usize,
18459 out_f: usize,
18460 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18461 if m_tokens == 1 {
18462 return self.linear(x, w, 1, in_f, out_f);
18463 }
18464 let xv = self.view(x, m_tokens * in_f);
18465 let mut y = self.alloc_uninit::<f32>(m_tokens * out_f)?;
18466 for t in 0..m_tokens {
18467 let row = xv.slice(t * in_f..(t + 1) * in_f);
18468 let mut xr = self.alloc_uninit::<f32>(in_f)?;
18469 self.copy_view_into(&mut xr, 0, &row, in_f)?;
18470 let yr = self.linear(&xr, w, 1, in_f, out_f)?;
18471 self.copy_into(&mut y, t * out_f, &yr, out_f)?;
18472 }
18473 Ok(y)
18474 }
18475
18476 pub fn linear(
18477 &self,
18478 x: &CudaSlice<f32>,
18479 w: &CudaSlice<f32>,
18480 m_tokens: usize,
18481 in_f: usize,
18482 out_f: usize,
18483 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
18484 self.linear_device(x, w, m_tokens, in_f, out_f)
18485 }
18486
18487 fn linear_device<I>(
18488 &self,
18489 x: &I,
18490 w: &I,
18491 m_tokens: usize,
18492 in_f: usize,
18493 out_f: usize,
18494 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>>
18495 where
18496 I: cudarc::driver::DevicePtr<f32>,
18497 {
18498 let mut c = self.alloc_uninit::<f32>(m_tokens * out_f)?; self.linear_device_into(x, w, &mut c, m_tokens, in_f, out_f)?;
18500 Ok(c)
18501 }
18502
18503 fn linear_device_into<I, O>(
18504 &self,
18505 x: &I,
18506 w: &I,
18507 c: &mut O,
18508 m_tokens: usize,
18509 in_f: usize,
18510 out_f: usize,
18511 ) -> Result<(), Box<dyn std::error::Error>>
18512 where
18513 I: cudarc::driver::DevicePtr<f32>,
18514 O: cudarc::driver::DevicePtrMut<f32>,
18515 {
18516 use cudarc::cublaslt::{Matmul, MatmulConfig};
18517 let cfg = MatmulConfig {
18518 transa: true,
18519 transb: false,
18520 transc: false,
18521 m: out_f as u64,
18522 n: m_tokens as u64,
18523 k: in_f as u64,
18524 alpha: 1.0,
18525 lda: in_f as i64,
18526 ldb: in_f as i64,
18527 beta: 0.0,
18528 ldc: out_f as i64,
18529 stride_a: None,
18530 stride_b: None,
18531 stride_c: None,
18532 stride_bias: None,
18533 batch_size: None,
18534 };
18535 let blas = self.gpu.blas();
18536 unsafe {
18537 blas.matmul(cfg, w, x, c, None, None)?;
18538 }
18539 Ok(())
18540 }
18541
18542 pub fn sdpa_naive(
18551 &self,
18552 q: &CudaSlice<f32>,
18553 k: &CudaSlice<f32>,
18554 v: &CudaSlice<f32>,
18555 o: &mut CudaSlice<f32>,
18556 head_dim: usize,
18557 n_head: usize,
18558 n_head_kv: usize,
18559 t: usize,
18560 t_kv: usize,
18561 scale: f32,
18562 causal: bool,
18563 ) -> Result<(), Box<dyn std::error::Error>> {
18564 if t_kv * 4 > SDPA_NAIVE_SMEM_MAX {
18565 return self.sdpa_naive_gmem(
18566 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
18567 );
18568 }
18569 let f = self.func("sdpa_naive_f32");
18570 let cfg = LaunchConfig {
18571 grid_dim: (n_head as u32, t as u32, 1),
18572 block_dim: (128, 1, 1),
18573 shared_mem_bytes: (t_kv * 4) as u32,
18574 };
18575 let (hd, nh, nhkv, ti, tkvi, cz) = (
18576 head_dim as i32,
18577 n_head as i32,
18578 n_head_kv as i32,
18579 t as i32,
18580 t_kv as i32,
18581 causal as i32,
18582 );
18583 let __s_b = self.gpu.stream();
18584 let mut b = __s_b.launch_builder(&f);
18585 b.arg(q)
18586 .arg(k)
18587 .arg(v)
18588 .arg(o)
18589 .arg(&hd)
18590 .arg(&nh)
18591 .arg(&nhkv)
18592 .arg(&ti)
18593 .arg(&tkvi)
18594 .arg(&scale)
18595 .arg(&cz);
18596 unsafe {
18597 b.launch(cfg)?;
18598 }
18599 Ok(())
18600 }
18601
18602 #[allow(clippy::too_many_arguments)]
18611 pub fn sdpa_naive_gmem(
18612 &self,
18613 q: &CudaSlice<f32>,
18614 k: &CudaSlice<f32>,
18615 v: &CudaSlice<f32>,
18616 o: &mut CudaSlice<f32>,
18617 head_dim: usize,
18618 n_head: usize,
18619 n_head_kv: usize,
18620 t: usize,
18621 t_kv: usize,
18622 scale: f32,
18623 causal: bool,
18624 ) -> Result<(), Box<dyn std::error::Error>> {
18625 let ws_len = n_head
18626 .checked_mul(t)
18627 .and_then(|x| x.checked_mul(t_kv))
18628 .ok_or("sdpa_naive_gmem: scores workspace size overflow")?;
18629 let ws_bytes = ws_len
18630 .checked_mul(std::mem::size_of::<f32>())
18631 .ok_or("sdpa_naive_gmem: scores workspace byte count overflow")?;
18632 if ws_bytes > SDPA_NAIVE_GMEM_WS_MAX {
18633 return Err(format!(
18634 "sdpa_naive_gmem: scores workspace {ws_bytes} bytes (heads {n_head} x T {t} x \
18635 T_kv {t_kv}) exceeds the {SDPA_NAIVE_GMEM_WS_MAX}-byte guard — this shape \
18636 needs a tiled/flash kernel, not the naive oracle"
18637 )
18638 .into());
18639 }
18640 let mut scores = self.uninit(ws_len)?;
18641 let f = self.func("sdpa_naive_gmem_f32");
18642 let cfg = LaunchConfig {
18643 grid_dim: (n_head as u32, t as u32, 1),
18644 block_dim: (128, 1, 1),
18645 shared_mem_bytes: 0,
18646 };
18647 let (hd, nh, nhkv, ti, tkvi, cz) = (
18648 head_dim as i32,
18649 n_head as i32,
18650 n_head_kv as i32,
18651 t as i32,
18652 t_kv as i32,
18653 causal as i32,
18654 );
18655 let __s_b = self.gpu.stream();
18656 let mut b = __s_b.launch_builder(&f);
18657 b.arg(q)
18658 .arg(k)
18659 .arg(v)
18660 .arg(o)
18661 .arg(&mut scores)
18662 .arg(&hd)
18663 .arg(&nh)
18664 .arg(&nhkv)
18665 .arg(&ti)
18666 .arg(&tkvi)
18667 .arg(&scale)
18668 .arg(&cz);
18669 unsafe {
18670 b.launch(cfg)?;
18671 }
18672 Ok(())
18673 }
18674
18675 #[allow(clippy::too_many_arguments)]
18680 pub fn sdpa_naive_island(
18681 &self,
18682 q: &CudaSlice<f32>,
18683 k: &CudaSlice<f32>,
18684 v: &CudaSlice<f32>,
18685 o: &mut CudaSlice<f32>,
18686 span_id: &CudaSlice<i32>,
18687 head_dim: usize,
18688 n_head: usize,
18689 n_head_kv: usize,
18690 t: usize,
18691 t_kv: usize,
18692 scale: f32,
18693 window: usize,
18694 ) -> Result<(), Box<dyn std::error::Error>> {
18695 let f = self.func("sdpa_naive_island_f32");
18696 let cfg = LaunchConfig {
18697 grid_dim: (n_head as u32, t as u32, 1),
18698 block_dim: (128, 1, 1),
18699 shared_mem_bytes: (t_kv * 4) as u32,
18700 };
18701 let (hd, nh, nhkv, ti, tkvi, wi) = (
18702 head_dim as i32,
18703 n_head as i32,
18704 n_head_kv as i32,
18705 t as i32,
18706 t_kv as i32,
18707 window as i32,
18708 );
18709 let __s_b = self.gpu.stream();
18710 let mut b = __s_b.launch_builder(&f);
18711 b.arg(q)
18712 .arg(k)
18713 .arg(v)
18714 .arg(o)
18715 .arg(span_id)
18716 .arg(&hd)
18717 .arg(&nh)
18718 .arg(&nhkv)
18719 .arg(&ti)
18720 .arg(&tkvi)
18721 .arg(&scale)
18722 .arg(&wi);
18723 unsafe {
18724 b.launch(cfg)?;
18725 }
18726 Ok(())
18727 }
18728
18729 #[allow(clippy::too_many_arguments)]
18731 pub fn sdpa_naive_w(
18732 &self,
18733 q: &CudaSlice<f32>,
18734 k: &CudaSlice<f32>,
18735 v: &CudaSlice<f32>,
18736 o: &mut CudaSlice<f32>,
18737 head_dim: usize,
18738 n_head: usize,
18739 n_head_kv: usize,
18740 t: usize,
18741 t_kv: usize,
18742 scale: f32,
18743 causal: bool,
18744 window: usize,
18745 ) -> Result<(), Box<dyn std::error::Error>> {
18746 let f = self.func("sdpa_naive_w_f32");
18747 let cfg = LaunchConfig {
18748 grid_dim: (n_head as u32, t as u32, 1),
18749 block_dim: (128, 1, 1),
18750 shared_mem_bytes: (t_kv * 4) as u32,
18751 };
18752 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
18753 head_dim as i32,
18754 n_head as i32,
18755 n_head_kv as i32,
18756 t as i32,
18757 t_kv as i32,
18758 causal as i32,
18759 window as i32,
18760 );
18761 let __s_b = self.gpu.stream();
18762 let mut b = __s_b.launch_builder(&f);
18763 b.arg(q)
18764 .arg(k)
18765 .arg(v)
18766 .arg(o)
18767 .arg(&hd)
18768 .arg(&nh)
18769 .arg(&nhkv)
18770 .arg(&ti)
18771 .arg(&tkvi)
18772 .arg(&scale)
18773 .arg(&cz)
18774 .arg(&wi);
18775 unsafe {
18776 b.launch(cfg)?;
18777 }
18778 Ok(())
18779 }
18780
18781 #[allow(clippy::too_many_arguments)]
18791 pub fn sdpa_naive_w_lo(
18792 &self,
18793 q: &CudaSlice<f32>,
18794 k: &CudaSlice<f32>,
18795 v: &CudaSlice<f32>,
18796 o: &mut CudaSlice<f32>,
18797 head_dim: usize,
18798 n_head: usize,
18799 n_head_kv: usize,
18800 t: usize,
18801 t_kv: usize,
18802 scale: f32,
18803 causal: bool,
18804 window: usize,
18805 ) -> Result<(), Box<dyn std::error::Error>> {
18806 let kv_lo = if window > 0 {
18807 (t_kv - t + 1).saturating_sub(window)
18808 } else {
18809 0
18810 };
18811 let smem = (t_kv - kv_lo) * 4;
18812 if smem > 48 * 1024 {
18813 return Err(format!(
18814 "sdpa_naive_w_lo: window {window} + T {t} rows need {smem} bytes of dynamic \
18815 shared memory (> 48KB launch bound) — this kernel clips the OLD side only; \
18816 a window this wide needs the multi-pass long-ctx kernel"
18817 )
18818 .into());
18819 }
18820 let f = self.func("sdpa_naive_w_lo_f32");
18821 let cfg = LaunchConfig {
18822 grid_dim: (n_head as u32, t as u32, 1),
18823 block_dim: (128, 1, 1),
18824 shared_mem_bytes: smem as u32,
18825 };
18826 let (hd, nh, nhkv, ti, tkvi, cz, wi, lo) = (
18827 head_dim as i32,
18828 n_head as i32,
18829 n_head_kv as i32,
18830 t as i32,
18831 t_kv as i32,
18832 causal as i32,
18833 window as i32,
18834 kv_lo as i32,
18835 );
18836 let __s_b = self.gpu.stream();
18837 let mut b = __s_b.launch_builder(&f);
18838 b.arg(q)
18839 .arg(k)
18840 .arg(v)
18841 .arg(o)
18842 .arg(&hd)
18843 .arg(&nh)
18844 .arg(&nhkv)
18845 .arg(&ti)
18846 .arg(&tkvi)
18847 .arg(&scale)
18848 .arg(&cz)
18849 .arg(&wi)
18850 .arg(&lo);
18851 unsafe {
18852 b.launch(cfg)?;
18853 }
18854 Ok(())
18855 }
18856
18857 pub fn sdpa_naive_view(
18859 &self,
18860 q: &CudaSlice<f32>,
18861 k: &cudarc::driver::CudaView<f32>,
18862 v: &cudarc::driver::CudaView<f32>,
18863 o: &mut CudaSlice<f32>,
18864 head_dim: usize,
18865 n_head: usize,
18866 n_head_kv: usize,
18867 t: usize,
18868 t_kv: usize,
18869 scale: f32,
18870 causal: bool,
18871 ) -> Result<(), Box<dyn std::error::Error>> {
18872 let f = self.func("sdpa_naive_f32");
18873 let cfg = LaunchConfig {
18874 grid_dim: (n_head as u32, t as u32, 1),
18875 block_dim: (128, 1, 1),
18876 shared_mem_bytes: (t_kv * 4) as u32,
18877 };
18878 let (hd, nh, nhkv, ti, tkvi, cz) = (
18879 head_dim as i32,
18880 n_head as i32,
18881 n_head_kv as i32,
18882 t as i32,
18883 t_kv as i32,
18884 causal as i32,
18885 );
18886 let __s_b = self.gpu.stream();
18887 let mut b = __s_b.launch_builder(&f);
18888 b.arg(q)
18889 .arg(k)
18890 .arg(v)
18891 .arg(o)
18892 .arg(&hd)
18893 .arg(&nh)
18894 .arg(&nhkv)
18895 .arg(&ti)
18896 .arg(&tkvi)
18897 .arg(&scale)
18898 .arg(&cz);
18899 unsafe {
18900 b.launch(cfg)?;
18901 }
18902 Ok(())
18903 }
18904
18905 #[allow(clippy::too_many_arguments)]
18913 pub fn fa_dequant_kv_view_f32(
18914 &self,
18915 k: &cudarc::driver::CudaView<u8>,
18916 v: &cudarc::driver::CudaView<u8>,
18917 kf: &mut CudaSlice<f32>,
18918 vf: &mut CudaSlice<f32>,
18919 kv_dim_k: usize,
18920 kv_dim_v: usize,
18921 t_kv: usize,
18922 k_tok_bytes: usize,
18923 v_tok_bytes: usize,
18924 g: bool,
18925 ) -> Result<(), Box<dyn std::error::Error>> {
18926 let f = if g {
18927 self.func_g("fa_dequant_kv_ws_f32")
18928 } else {
18929 self.func("fa_dequant_kv_ws_f32")
18930 };
18931 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
18932 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
18933 let cfg = LaunchConfig {
18934 grid_dim: (nblk.max(1), 1, 1),
18935 block_dim: (256, 1, 1),
18936 shared_mem_bytes: 0,
18937 };
18938 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
18939 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
18940 let __s_b = self.gpu.stream();
18941 let mut b = __s_b.launch_builder(&f);
18942 b.arg(k)
18943 .arg(v)
18944 .arg(&mut *kf)
18945 .arg(&mut *vf)
18946 .arg(&kdk)
18947 .arg(&kdv)
18948 .arg(&tkvi)
18949 .arg(&ktb)
18950 .arg(&vtb);
18951 unsafe {
18952 b.launch(cfg)?;
18953 }
18954 Ok(())
18955 }
18956
18957 #[allow(clippy::too_many_arguments)]
18958 pub fn sdpa_naive_quantized_view(
18959 &self,
18960 q: &CudaSlice<f32>,
18961 k: &cudarc::driver::CudaView<u8>,
18962 v: &cudarc::driver::CudaView<u8>,
18963 o: &mut CudaSlice<f32>,
18964 head_dim: usize,
18965 n_head: usize,
18966 n_head_kv: usize,
18967 t: usize,
18968 t_kv: usize,
18969 scale: f32,
18970 causal: bool,
18971 k_tok_bytes: usize,
18972 v_tok_bytes: usize,
18973 ) -> Result<(), Box<dyn std::error::Error>> {
18974 let kv_dim = n_head_kv * head_dim;
18975 let mut kf = self.uninit(t_kv * kv_dim)?;
18976 let mut vf = self.uninit(t_kv * kv_dim)?;
18977 let f = self.func("fa_dequant_kv_ws_f32");
18978 let total = (2 * t_kv * kv_dim) as u64;
18979 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
18980 let cfg = LaunchConfig {
18981 grid_dim: (nblk.max(1), 1, 1),
18982 block_dim: (256, 1, 1),
18983 shared_mem_bytes: 0,
18984 };
18985 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
18986 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
18987 let __s_b = self.gpu.stream();
18988 let mut b = __s_b.launch_builder(&f);
18989 b.arg(k)
18990 .arg(v)
18991 .arg(&mut kf)
18992 .arg(&mut vf)
18993 .arg(&kv_dim_i)
18994 .arg(&kv_dim_i)
18995 .arg(&t_kv_i)
18996 .arg(&k_tok_bytes_i)
18997 .arg(&v_tok_bytes_i);
18998 unsafe { b.launch(cfg)? };
18999 self.sdpa_naive(
19000 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
19001 )
19002 }
19003
19004 #[allow(clippy::too_many_arguments)]
19016 pub fn sdpa_naive_w_quantized_view(
19017 &self,
19018 q: &CudaSlice<f32>,
19019 k: &cudarc::driver::CudaView<u8>,
19020 v: &cudarc::driver::CudaView<u8>,
19021 o: &mut CudaSlice<f32>,
19022 head_dim: usize,
19023 n_head: usize,
19024 n_head_kv: usize,
19025 t: usize,
19026 t_kv: usize,
19027 scale: f32,
19028 causal: bool,
19029 window: usize,
19030 k_tok_bytes: usize,
19031 v_tok_bytes: usize,
19032 ) -> Result<(), Box<dyn std::error::Error>> {
19033 let kv_dim = n_head_kv * head_dim;
19034 let mut kf = self.uninit(t_kv * kv_dim)?;
19035 let mut vf = self.uninit(t_kv * kv_dim)?;
19036 let f = self.func("fa_dequant_kv_ws_f32");
19037 let total = (2 * t_kv * kv_dim) as u64;
19038 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
19039 let cfg = LaunchConfig {
19040 grid_dim: (nblk.max(1), 1, 1),
19041 block_dim: (256, 1, 1),
19042 shared_mem_bytes: 0,
19043 };
19044 let (kv_dim_i, t_kv_i) = (kv_dim as i32, t_kv as i32);
19045 let (k_tok_bytes_i, v_tok_bytes_i) = (k_tok_bytes as i64, v_tok_bytes as i64);
19046 let __s_b = self.gpu.stream();
19047 let mut b = __s_b.launch_builder(&f);
19048 b.arg(k)
19049 .arg(v)
19050 .arg(&mut kf)
19051 .arg(&mut vf)
19052 .arg(&kv_dim_i)
19053 .arg(&kv_dim_i)
19054 .arg(&t_kv_i)
19055 .arg(&k_tok_bytes_i)
19056 .arg(&v_tok_bytes_i);
19057 unsafe { b.launch(cfg)? };
19058 self.sdpa_naive_w(
19059 q, &kf, &vf, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
19060 )
19061 }
19062
19063 pub fn fa_prefill(
19067 &self,
19068 q: &CudaSlice<f32>,
19069 k: &CudaSlice<f32>,
19070 v: &CudaSlice<f32>,
19071 o: &mut CudaSlice<f32>,
19072 head_dim: usize,
19073 n_head: usize,
19074 n_head_kv: usize,
19075 t: usize,
19076 t_kv: usize,
19077 scale: f32,
19078 causal: bool,
19079 ) -> Result<(), Box<dyn std::error::Error>> {
19080 if portable_mma_gated() {
19081 return self.sdpa_naive(
19082 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
19083 );
19084 }
19085 let fa3_on = head_dim == 256
19093 && causal
19094 && t == t_kv
19095 && match std::env::var("MEMRA_FA3").as_deref() {
19096 Ok("0") => false,
19097 Ok("1") => {
19101 refuse_portable_force("MEMRA_FA3=1", "the sm_90a fa3/bf16 kernels");
19102 true
19103 }
19104 _ => cfg!(memra_hopper_mma),
19105 };
19106 if fa3_on {
19107 let n = t * n_head * head_dim;
19108 let nkv = t * n_head_kv * head_dim;
19109 let mut q16 = self.alloc_u8_uninit(n * 2)?;
19110 let mut k16 = self.alloc_u8_uninit(nkv * 2)?;
19111 let mut v16 = self.alloc_u8_uninit(nkv * 2)?;
19112 self.f32_to_bf16_into(q, &mut q16, n)?;
19113 self.f32_to_bf16_into(k, &mut k16, nkv)?;
19114 self.f32_to_bf16_into(v, &mut v16, nkv)?;
19115 let rc = {
19116 use cudarc::driver::{DevicePtr, DevicePtrMut};
19117 let stream = self.gpu.stream();
19118 let (qp, _g1) = q16.device_ptr(&stream);
19119 let (kp, _g2) = k16.device_ptr(&stream);
19120 let (vp, _g3) = v16.device_ptr(&stream);
19121 let (op, _g4) = o.device_ptr_mut(&stream);
19122 unsafe {
19123 memra_fa3_prefill(
19124 qp as *const core::ffi::c_void,
19125 kp as *const core::ffi::c_void,
19126 vp as *const core::ffi::c_void,
19127 op as *mut f32,
19128 t as i32,
19129 n_head as i32,
19130 n_head_kv as i32,
19131 head_dim as i32,
19132 scale,
19133 stream.cu_stream() as *mut core::ffi::c_void,
19134 )
19135 }
19136 };
19137 if rc != 0 {
19138 return Err(format!("memra_fa3_prefill rc={rc}").into());
19139 }
19140 return Ok(());
19141 }
19142 static FA_P1: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19147 let fa_p1 = *FA_P1.get_or_init(|| std::env::var("MEMRA_FA_P1").as_deref() == Ok("1"));
19148 if fa_p1 && head_dim == 256 && !std::env::var("MEMRA_FA_FLOOR").is_ok() {
19149 const BLOCK_Q: usize = 64;
19150 const BKX: usize = 32;
19151 let f = self.func("fa_prefill_bf16_p1");
19152 let shmem = (2 * (2 * BKX * head_dim + BLOCK_Q * BKX)
19153 + 4 * (BLOCK_Q * BKX + 2 * BLOCK_Q)) as u32;
19154 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19155 f.set_attribute(
19156 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19157 shmem as i32,
19158 )?;
19159 let cfg = LaunchConfig {
19160 grid_dim: (
19161 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
19162 n_head as u32,
19163 1,
19164 ),
19165 block_dim: (32, 4, 1),
19166 shared_mem_bytes: shmem,
19167 };
19168 let (hd, nh, nhkv, ti, tkvi, cz) = (
19169 head_dim as i32,
19170 n_head as i32,
19171 n_head_kv as i32,
19172 t as i32,
19173 t_kv as i32,
19174 causal as i32,
19175 );
19176 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
19177 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
19178 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
19179 let __s_b = self.gpu.stream();
19180 let mut b = __s_b.launch_builder(&f);
19181 b.arg(&qb)
19182 .arg(&kb)
19183 .arg(&vb)
19184 .arg(o)
19185 .arg(&hd)
19186 .arg(&nh)
19187 .arg(&nhkv)
19188 .arg(&ti)
19189 .arg(&tkvi)
19190 .arg(&scale)
19191 .arg(&cz);
19192 unsafe {
19193 b.launch(cfg)?;
19194 }
19195 return Ok(());
19196 }
19197 const BK: usize = 32;
19203 let w2 = std::env::var("MEMRA_FA_PP_W2").as_deref() == Ok("1");
19206 let (block_q, warps, w2_sfx): (usize, u32, &str) =
19207 if w2 { (32, 2, "_w2") } else { (64, 4, "") };
19208 let hd_sfx = fa_hd_suffix(head_dim)?;
19212 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
19213 let bf16kv = !floor && !w2 && std::env::var("MEMRA_FA_BF16KV").as_deref() != Ok("0");
19218 let (kb16, vb16) = if bf16kv {
19219 let n = t_kv * n_head_kv * head_dim;
19220 let mut kb = self.alloc_u8_uninit(n * 2)?;
19221 let mut vb = self.alloc_u8_uninit(n * 2)?;
19222 let fcv = self.func("f32_to_bf16_bulk");
19223 let ni = n as i64;
19224 let cfgc = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
19225 let __s_b = self.gpu.stream();
19226 let mut b = __s_b.launch_builder(&fcv);
19227 b.arg(k).arg(&mut kb).arg(&ni);
19228 unsafe {
19229 b.launch(cfgc)?;
19230 }
19231 let __s_b = self.gpu.stream();
19232 let mut b = __s_b.launch_builder(&fcv);
19233 b.arg(v).arg(&mut vb).arg(&ni);
19234 unsafe {
19235 b.launch(cfgc)?;
19236 }
19237 (Some(kb), Some(vb))
19238 } else {
19239 (None, None)
19240 };
19241 let f = self.func(&if bf16kv {
19242 format!("fa_prefill_bf16kv_pp{hd_sfx}")
19243 } else {
19244 format!(
19245 "fa_prefill_f32{}{}{hd_sfx}",
19246 if floor { "" } else { "_pp" },
19247 if floor { "" } else { w2_sfx }
19248 )
19249 });
19250 let kv_stages = if bf16kv { 2 } else { 1 };
19253 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
19254 + 4 * (block_q * BK + 2 * block_q)) as u32;
19255 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19256 f.set_attribute(
19257 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19258 shmem as i32,
19259 )?;
19260 let cfg = LaunchConfig {
19261 grid_dim: (
19262 (t as u32 + block_q as u32 - 1) / block_q as u32,
19263 n_head as u32,
19264 1,
19265 ),
19266 block_dim: (32, warps, 1),
19267 shared_mem_bytes: shmem,
19268 };
19269 let (hd, nh, nhkv, ti, tkvi, cz) = (
19270 head_dim as i32,
19271 n_head as i32,
19272 n_head_kv as i32,
19273 t as i32,
19274 t_kv as i32,
19275 causal as i32,
19276 );
19277 let __s_b = self.gpu.stream();
19278 let mut b = __s_b.launch_builder(&f);
19279 b.arg(q);
19280 match (&kb16, &vb16) {
19281 (Some(kb), Some(vb)) => {
19282 b.arg(kb).arg(vb);
19283 }
19284 _ => {
19285 b.arg(k).arg(v);
19286 }
19287 }
19288 b.arg(o)
19289 .arg(&hd)
19290 .arg(&nh)
19291 .arg(&nhkv)
19292 .arg(&ti)
19293 .arg(&tkvi)
19294 .arg(&scale)
19295 .arg(&cz);
19296 unsafe {
19297 b.launch(cfg)?;
19298 }
19299 Ok(())
19300 }
19301
19302 #[allow(clippy::too_many_arguments)]
19306 pub fn fa_prefill_w(
19307 &self,
19308 q: &CudaSlice<f32>,
19309 k: &CudaSlice<f32>,
19310 v: &CudaSlice<f32>,
19311 o: &mut CudaSlice<f32>,
19312 head_dim: usize,
19313 n_head: usize,
19314 n_head_kv: usize,
19315 t: usize,
19316 t_kv: usize,
19317 scale: f32,
19318 causal: bool,
19319 window: usize,
19320 ) -> Result<(), Box<dyn std::error::Error>> {
19321 if portable_mma_gated() {
19324 return self.sdpa_naive_w(
19325 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal, window,
19326 );
19327 }
19328 static FAW_F32: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19332 let faw_f32 =
19333 *FAW_F32.get_or_init(|| std::env::var("MEMRA_FAW_STAGE").as_deref() == Ok("f32"));
19334 let floor = std::env::var("MEMRA_FA_FLOOR").is_ok();
19335 self.fa_prefill_w_arm(
19336 q,
19337 k,
19338 v,
19339 o,
19340 head_dim,
19341 n_head,
19342 n_head_kv,
19343 t,
19344 t_kv,
19345 scale,
19346 causal,
19347 window,
19348 floor || faw_f32,
19349 floor,
19350 )
19351 }
19352
19353 #[allow(clippy::too_many_arguments)]
19356 pub fn fa_prefill_w_pre(
19357 &self,
19358 qb: &CudaSlice<u8>,
19359 kb: &CudaSlice<u8>,
19360 vb: &CudaSlice<u8>,
19361 o: &mut CudaSlice<f32>,
19362 head_dim: usize,
19363 n_head: usize,
19364 n_head_kv: usize,
19365 t: usize,
19366 t_kv: usize,
19367 scale: f32,
19368 causal: bool,
19369 window: usize,
19370 v_f16: bool,
19371 ) -> Result<(), Box<dyn std::error::Error>> {
19372 const BLOCK_Q: usize = 64;
19373 const BK: usize = 32;
19374 debug_assert_eq!(head_dim, 256);
19375 let hp = fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
19376 debug_assert!(!v_f16 || hp, "f16 V emitted but the SWA hp arm is off");
19377 if hp {
19378 const BLOCK_QH: usize = 32;
19379 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
19382 let vh: &CudaSlice<u8> = if v_f16 {
19383 vb
19384 } else {
19385 let n = t_kv * n_head_kv * head_dim;
19386 if vguard.as_ref().map(|b| b.len() < n * 2).unwrap_or(true) {
19387 *vguard = Some(self.alloc_uninit::<u8>(n * 2)?);
19388 }
19389 self.bf16_to_f16_into(vb, n, vguard.as_mut().unwrap())?;
19390 vguard.as_ref().unwrap()
19391 };
19392 let f = self.func("fa_prefill_w_bf16_p1h2");
19393 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
19394 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19395 f.set_attribute(
19396 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19397 shmem as i32,
19398 )?;
19399 let cfg = LaunchConfig {
19400 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
19401 block_dim: (32, 4, 1),
19402 shared_mem_bytes: shmem,
19403 };
19404 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
19405 head_dim as i32,
19406 n_head as i32,
19407 n_head_kv as i32,
19408 t as i32,
19409 t_kv as i32,
19410 causal as i32,
19411 window as i32,
19412 );
19413 let __s_b = self.gpu.stream();
19414 let mut b = __s_b.launch_builder(&f);
19415 b.arg(qb)
19416 .arg(kb)
19417 .arg(vh)
19418 .arg(o)
19419 .arg(&hd)
19420 .arg(&nh)
19421 .arg(&nhkv)
19422 .arg(&ti)
19423 .arg(&tkvi)
19424 .arg(&scale)
19425 .arg(&cz)
19426 .arg(&wi);
19427 unsafe {
19428 b.launch(cfg)?;
19429 }
19430 return Ok(());
19431 }
19432 let f = self.func("fa_prefill_w_bf16_p1");
19433 let shmem =
19434 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
19435 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19436 f.set_attribute(
19437 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19438 shmem as i32,
19439 )?;
19440 let cfg = LaunchConfig {
19441 grid_dim: (
19442 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
19443 n_head as u32,
19444 1,
19445 ),
19446 block_dim: (32, 4, 1),
19447 shared_mem_bytes: shmem,
19448 };
19449 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
19450 head_dim as i32,
19451 n_head as i32,
19452 n_head_kv as i32,
19453 t as i32,
19454 t_kv as i32,
19455 causal as i32,
19456 window as i32,
19457 );
19458 let __s_b = self.gpu.stream();
19459 let mut b = __s_b.launch_builder(&f);
19460 b.arg(qb)
19461 .arg(kb)
19462 .arg(vb)
19463 .arg(o)
19464 .arg(&hd)
19465 .arg(&nh)
19466 .arg(&nhkv)
19467 .arg(&ti)
19468 .arg(&tkvi)
19469 .arg(&scale)
19470 .arg(&cz)
19471 .arg(&wi);
19472 unsafe {
19473 b.launch(cfg)?;
19474 }
19475 Ok(())
19476 }
19477
19478 #[allow(clippy::too_many_arguments)]
19480 pub fn fa_prefill_w_arm(
19481 &self,
19482 q: &CudaSlice<f32>,
19483 k: &CudaSlice<f32>,
19484 v: &CudaSlice<f32>,
19485 o: &mut CudaSlice<f32>,
19486 head_dim: usize,
19487 n_head: usize,
19488 n_head_kv: usize,
19489 t: usize,
19490 t_kv: usize,
19491 scale: f32,
19492 causal: bool,
19493 window: usize,
19494 f32_stage: bool,
19495 floor: bool,
19496 ) -> Result<(), Box<dyn std::error::Error>> {
19497 const BLOCK_Q: usize = 64;
19498 const BK: usize = 32;
19499 debug_assert_eq!(head_dim, 256, "fa_prefill_w is stamped hd256 only");
19500 static P1_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19504 let p1 = !floor
19505 && !f32_stage
19506 && *P1_ON.get_or_init(|| {
19507 std::env::var("MEMRA_FAW_P1")
19508 .map(|v| v != "0")
19509 .unwrap_or(true)
19510 });
19511 let hp =
19512 p1 && fa_f16pv_on() && faw_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
19513 if hp {
19514 const BLOCK_QH: usize = 32;
19515 let f = self.func("fa_prefill_w_bf16_p1h2");
19516 let shmem = (2 * (2 * BK * head_dim + 2 * BLOCK_QH * BK) + 4 * (2 * BLOCK_QH)) as u32;
19517 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19518 f.set_attribute(
19519 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19520 shmem as i32,
19521 )?;
19522 let cfg = LaunchConfig {
19523 grid_dim: ((t as u32).div_ceil(BLOCK_QH as u32), (n_head / 2) as u32, 1),
19524 block_dim: (32, 4, 1),
19525 shared_mem_bytes: shmem,
19526 };
19527 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
19528 head_dim as i32,
19529 n_head as i32,
19530 n_head_kv as i32,
19531 t as i32,
19532 t_kv as i32,
19533 causal as i32,
19534 window as i32,
19535 );
19536 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
19537 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
19538 let vh = self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?;
19539 let __s_b = self.gpu.stream();
19540 let mut b = __s_b.launch_builder(&f);
19541 b.arg(&qb)
19542 .arg(&kb)
19543 .arg(&vh)
19544 .arg(o)
19545 .arg(&hd)
19546 .arg(&nh)
19547 .arg(&nhkv)
19548 .arg(&ti)
19549 .arg(&tkvi)
19550 .arg(&scale)
19551 .arg(&cz)
19552 .arg(&wi);
19553 unsafe {
19554 b.launch(cfg)?;
19555 }
19556 return Ok(());
19557 }
19558 if p1 {
19559 let f = self.func("fa_prefill_w_bf16_p1");
19560 let shmem =
19561 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
19562 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19563 f.set_attribute(
19564 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19565 shmem as i32,
19566 )?;
19567 let cfg = LaunchConfig {
19568 grid_dim: (
19569 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
19570 n_head as u32,
19571 1,
19572 ),
19573 block_dim: (32, 4, 1),
19574 shared_mem_bytes: shmem,
19575 };
19576 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
19577 head_dim as i32,
19578 n_head as i32,
19579 n_head_kv as i32,
19580 t as i32,
19581 t_kv as i32,
19582 causal as i32,
19583 window as i32,
19584 );
19585 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
19586 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
19587 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
19588 let __s_b = self.gpu.stream();
19589 let mut b = __s_b.launch_builder(&f);
19590 b.arg(&qb)
19591 .arg(&kb)
19592 .arg(&vb)
19593 .arg(o)
19594 .arg(&hd)
19595 .arg(&nh)
19596 .arg(&nhkv)
19597 .arg(&ti)
19598 .arg(&tkvi)
19599 .arg(&scale)
19600 .arg(&cz)
19601 .arg(&wi);
19602 unsafe {
19603 b.launch(cfg)?;
19604 }
19605 return Ok(());
19606 }
19607 static G4_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19610 let g4 = !floor
19611 && !f32_stage
19612 && n_head_kv == 1
19613 && n_head % 4 == 0
19614 && *G4_ON.get_or_init(|| {
19615 std::env::var("MEMRA_FAW_G4")
19616 .map(|v| v != "0")
19617 .unwrap_or(true)
19618 });
19619 if g4 {
19620 const SP_M: usize = 16;
19621 static O2_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19624 let o2 = *O2_ON.get_or_init(|| {
19625 std::env::var("MEMRA_FAW_O2")
19626 .map(|v| v != "0")
19627 .unwrap_or(true)
19628 });
19629 let f = self.func(if o2 {
19630 "fa_prefill_w_bf16_g4o2"
19631 } else {
19632 "fa_prefill_w_bf16_g4"
19633 });
19634 let shmem = if o2 {
19635 (2 * (4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M)) as u32
19636 } else {
19637 (2 * (2 * BK * head_dim + 4 * SP_M * head_dim + 4 * SP_M * BK) + 4 * (4 * SP_M))
19638 as u32
19639 };
19640 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19641 f.set_attribute(
19642 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19643 shmem as i32,
19644 )?;
19645 let cfg = LaunchConfig {
19646 grid_dim: ((t as u32).div_ceil(SP_M as u32), (n_head / 4) as u32, 1),
19647 block_dim: (32, 4, 1),
19648 shared_mem_bytes: shmem,
19649 };
19650 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
19651 head_dim as i32,
19652 n_head as i32,
19653 n_head_kv as i32,
19654 t as i32,
19655 t_kv as i32,
19656 causal as i32,
19657 window as i32,
19658 );
19659 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
19660 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
19661 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
19662 let __s_b = self.gpu.stream();
19663 let mut b = __s_b.launch_builder(&f);
19664 b.arg(&qb)
19665 .arg(&kb)
19666 .arg(&vb)
19667 .arg(o)
19668 .arg(&hd)
19669 .arg(&nh)
19670 .arg(&nhkv)
19671 .arg(&ti)
19672 .arg(&tkvi)
19673 .arg(&scale)
19674 .arg(&cz)
19675 .arg(&wi);
19676 unsafe {
19677 b.launch(cfg)?;
19678 }
19679 return Ok(());
19680 }
19681 let f = self.func(if floor {
19682 "fa_prefill_w_f32"
19683 } else if f32_stage {
19684 "fa_prefill_w_f32_pp"
19685 } else {
19686 "fa_prefill_w_bf16_pp"
19687 });
19688 let shmem =
19689 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
19690 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19691 f.set_attribute(
19692 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19693 shmem as i32,
19694 )?;
19695 let cfg = LaunchConfig {
19696 grid_dim: (
19697 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
19698 n_head as u32,
19699 1,
19700 ),
19701 block_dim: (32, 4, 1),
19702 shared_mem_bytes: shmem,
19703 };
19704 let (hd, nh, nhkv, ti, tkvi, cz, wi) = (
19705 head_dim as i32,
19706 n_head as i32,
19707 n_head_kv as i32,
19708 t as i32,
19709 t_kv as i32,
19710 causal as i32,
19711 window as i32,
19712 );
19713 if f32_stage {
19714 let __s_b = self.gpu.stream();
19715 let mut b = __s_b.launch_builder(&f);
19716 b.arg(q)
19717 .arg(k)
19718 .arg(v)
19719 .arg(o)
19720 .arg(&hd)
19721 .arg(&nh)
19722 .arg(&nhkv)
19723 .arg(&ti)
19724 .arg(&tkvi)
19725 .arg(&scale)
19726 .arg(&cz)
19727 .arg(&wi);
19728 unsafe {
19729 b.launch(cfg)?;
19730 }
19731 } else {
19732 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
19733 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
19734 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
19735 let __s_b = self.gpu.stream();
19736 let mut b = __s_b.launch_builder(&f);
19737 b.arg(&qb)
19738 .arg(&kb)
19739 .arg(&vb)
19740 .arg(o)
19741 .arg(&hd)
19742 .arg(&nh)
19743 .arg(&nhkv)
19744 .arg(&ti)
19745 .arg(&tkvi)
19746 .arg(&scale)
19747 .arg(&cz)
19748 .arg(&wi);
19749 unsafe {
19750 b.launch(cfg)?;
19751 }
19752 }
19753 Ok(())
19754 }
19755
19756 #[allow(clippy::too_many_arguments)]
19760 pub fn fa_prefill_hd512(
19761 &self,
19762 q: &CudaSlice<f32>,
19763 k: &CudaSlice<f32>,
19764 v: &CudaSlice<f32>,
19765 o: &mut CudaSlice<f32>,
19766 head_dim: usize,
19767 n_head: usize,
19768 n_head_kv: usize,
19769 t: usize,
19770 t_kv: usize,
19771 scale: f32,
19772 causal: bool,
19773 ) -> Result<(), Box<dyn std::error::Error>> {
19774 if portable_mma_gated() {
19776 return self.sdpa_naive(
19777 q, k, v, o, head_dim, n_head, n_head_kv, t, t_kv, scale, causal,
19778 );
19779 }
19780 static F32_STAGE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19786 let f32_stage =
19787 *F32_STAGE.get_or_init(|| std::env::var("MEMRA_FA512_STAGE").as_deref() == Ok("f32"));
19788 static SP_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
19792 let sp = !f32_stage
19793 && *SP_ON.get_or_init(|| {
19794 std::env::var("MEMRA_FA512_SP")
19795 .map(|v| v != "0")
19796 .unwrap_or(true)
19797 });
19798 self.fa_prefill_hd512_arm(
19799 q,
19800 k,
19801 v,
19802 o,
19803 head_dim,
19804 n_head,
19805 n_head_kv,
19806 t,
19807 t_kv,
19808 scale,
19809 causal,
19810 f32_stage,
19811 sp,
19812 sp && fa_f16pv_on(),
19813 )
19814 }
19815
19816 #[allow(clippy::too_many_arguments)]
19818 pub fn fa_prefill_hd512_pre(
19819 &self,
19820 qb: &CudaSlice<u8>,
19821 kb: &CudaSlice<u8>,
19822 vb: &CudaSlice<u8>,
19823 o: &mut CudaSlice<f32>,
19824 head_dim: usize,
19825 n_head: usize,
19826 n_head_kv: usize,
19827 t: usize,
19828 t_kv: usize,
19829 scale: f32,
19830 causal: bool,
19831 v_f16: bool,
19832 ) -> Result<(), Box<dyn std::error::Error>> {
19833 debug_assert_eq!(head_dim, 512);
19834 const SP_M: usize = 16;
19835 const BKS: usize = 32;
19836 let f16pv = fa_f16pv_on();
19840 let nw = if f16pv { fa512_wide_warps() } else { 2 };
19841 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
19842 debug_assert!(!v_f16 || f16pv, "f16 V emitted without the door on");
19843 let mut vguard = self.fa_vf16_scratch.lock().unwrap();
19844 let vref: &CudaSlice<u8> = if f16pv && !v_f16 {
19845 let n = t_kv * n_head_kv * head_dim;
19847 let need = n * 2;
19848 if vguard.as_ref().map(|b| b.len() < need).unwrap_or(true) {
19849 *vguard = Some(self.alloc_uninit::<u8>(need)?);
19850 }
19851 let dst = vguard.as_mut().unwrap();
19852 self.bf16_to_f16_into(vb, n, dst)?;
19853 vguard.as_ref().unwrap()
19854 } else {
19855 vb
19856 };
19857 let f = self.func(if hp {
19858 "fa_prefill_bf16_hd512_sp16h2"
19859 } else {
19860 match (f16pv, nw) {
19861 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
19862 (true, _) => "fa_prefill_bf16_hd512_sp16",
19863 _ => "fa_prefill_bf16_hd512_sp",
19864 }
19865 });
19866 let (nwarp, npart) = if hp {
19867 (4usize, 4usize)
19868 } else if nw > 2 {
19869 (nw, nw)
19870 } else {
19871 (2, 1)
19872 };
19873 let shmem = if hp {
19875 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS) + 4 * (2 * npart * SP_M * BKS + 2 * SP_M))
19876 as u32
19877 } else {
19878 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
19879 + 4 * (npart * SP_M * BKS + SP_M)) as u32
19880 };
19881 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19882 f.set_attribute(
19883 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19884 shmem as i32,
19885 )?;
19886 let grid_y = if hp {
19887 (n_head / 2) as u32
19888 } else {
19889 n_head as u32
19890 };
19891 let cfg = LaunchConfig {
19892 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
19893 block_dim: (32, nwarp as u32, 1),
19894 shared_mem_bytes: shmem,
19895 };
19896 let (hd, nh, nhkv, ti, tkvi, cz) = (
19897 head_dim as i32,
19898 n_head as i32,
19899 n_head_kv as i32,
19900 t as i32,
19901 t_kv as i32,
19902 causal as i32,
19903 );
19904 let __s_b = self.gpu.stream();
19905 let mut b = __s_b.launch_builder(&f);
19906 b.arg(qb)
19907 .arg(kb)
19908 .arg(vref)
19909 .arg(o)
19910 .arg(&hd)
19911 .arg(&nh)
19912 .arg(&nhkv)
19913 .arg(&ti)
19914 .arg(&tkvi)
19915 .arg(&scale)
19916 .arg(&cz);
19917 unsafe {
19918 b.launch(cfg)?;
19919 }
19920 Ok(())
19921 }
19922
19923 #[allow(clippy::too_many_arguments)]
19926 pub fn fa_prefill_hd512_arm(
19927 &self,
19928 q: &CudaSlice<f32>,
19929 k: &CudaSlice<f32>,
19930 v: &CudaSlice<f32>,
19931 o: &mut CudaSlice<f32>,
19932 head_dim: usize,
19933 n_head: usize,
19934 n_head_kv: usize,
19935 t: usize,
19936 t_kv: usize,
19937 scale: f32,
19938 causal: bool,
19939 f32_stage: bool,
19940 sp: bool,
19941 f16pv: bool,
19942 ) -> Result<(), Box<dyn std::error::Error>> {
19943 debug_assert_eq!(head_dim, 512, "fa_prefill_hd512 is hd512 only");
19944 if sp && !f32_stage {
19945 const SP_M: usize = 16;
19949 const BKS: usize = 32;
19950 let nw = if f16pv { fa512_wide_warps() } else { 2 };
19951 let hp = f16pv && fa512_hp_on() && n_head % 2 == 0 && (n_head / n_head_kv) % 2 == 0;
19952 let f = self.func(if hp {
19953 "fa_prefill_bf16_hd512_sp16h2"
19954 } else {
19955 match (f16pv, nw) {
19956 (true, 4) => "fa_prefill_bf16_hd512_sp16w4",
19957 (true, _) => "fa_prefill_bf16_hd512_sp16",
19958 _ => "fa_prefill_bf16_hd512_sp",
19959 }
19960 });
19961 let (nwarp, npart) = if hp {
19962 (4usize, 4usize)
19963 } else if nw > 2 {
19964 (nw, nw)
19965 } else {
19966 (2, 1)
19967 };
19968 let shmem = if hp {
19969 (2 * (2 * BKS * head_dim + 2 * SP_M * BKS)
19970 + 4 * (2 * npart * SP_M * BKS + 2 * SP_M)) as u32
19971 } else {
19972 (2 * (SP_M * head_dim + 2 * BKS * head_dim + SP_M * BKS)
19973 + 4 * (npart * SP_M * BKS + SP_M)) as u32
19974 };
19975 use cudarc::driver::sys::CUfunction_attribute_enum as A;
19976 f.set_attribute(
19977 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
19978 shmem as i32,
19979 )?;
19980 let grid_y = if hp {
19981 (n_head / 2) as u32
19982 } else {
19983 n_head as u32
19984 };
19985 let cfg = LaunchConfig {
19986 grid_dim: ((t as u32).div_ceil(SP_M as u32), grid_y, 1),
19987 block_dim: (32, nwarp as u32, 1),
19988 shared_mem_bytes: shmem,
19989 };
19990 let (hd, nh, nhkv, ti, tkvi, cz) = (
19991 head_dim as i32,
19992 n_head as i32,
19993 n_head_kv as i32,
19994 t as i32,
19995 t_kv as i32,
19996 causal as i32,
19997 );
19998 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
19999 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
20000 let vb = if f16pv {
20001 self.f32_to_f16(v, t_kv * n_head_kv * head_dim)?
20002 } else {
20003 self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?
20004 };
20005 let __s_b = self.gpu.stream();
20006 let mut b = __s_b.launch_builder(&f);
20007 b.arg(&qb)
20008 .arg(&kb)
20009 .arg(&vb)
20010 .arg(o)
20011 .arg(&hd)
20012 .arg(&nh)
20013 .arg(&nhkv)
20014 .arg(&ti)
20015 .arg(&tkvi)
20016 .arg(&scale)
20017 .arg(&cz);
20018 unsafe {
20019 b.launch(cfg)?;
20020 }
20021 return Ok(());
20022 }
20023 const BLOCK_Q: usize = 32;
20024 const BK: usize = 32;
20025 const HALF: usize = 256;
20026 let f = self.func(if f32_stage {
20027 "fa_prefill_f32_hd512"
20028 } else {
20029 "fa_prefill_bf16_hd512"
20030 });
20031 let shmem = (2 * (BLOCK_Q * head_dim + BK * head_dim + BK * HALF + BLOCK_Q * BK)
20033 + 4 * BLOCK_Q) as u32;
20034 use cudarc::driver::sys::CUfunction_attribute_enum as A;
20035 f.set_attribute(
20036 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20037 shmem as i32,
20038 )?;
20039 let cfg = LaunchConfig {
20040 grid_dim: (
20041 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
20042 n_head as u32,
20043 2,
20044 ),
20045 block_dim: (32, 2, 1),
20046 shared_mem_bytes: shmem,
20047 };
20048 let (hd, nh, nhkv, ti, tkvi, cz) = (
20049 head_dim as i32,
20050 n_head as i32,
20051 n_head_kv as i32,
20052 t as i32,
20053 t_kv as i32,
20054 causal as i32,
20055 );
20056 if f32_stage {
20057 let __s_b = self.gpu.stream();
20058 let mut b = __s_b.launch_builder(&f);
20059 b.arg(q)
20060 .arg(k)
20061 .arg(v)
20062 .arg(o)
20063 .arg(&hd)
20064 .arg(&nh)
20065 .arg(&nhkv)
20066 .arg(&ti)
20067 .arg(&tkvi)
20068 .arg(&scale)
20069 .arg(&cz);
20070 unsafe {
20071 b.launch(cfg)?;
20072 }
20073 } else {
20074 let qb = self.f32_to_bf16(q, t * n_head * head_dim)?;
20075 let kb = self.f32_to_bf16(k, t_kv * n_head_kv * head_dim)?;
20076 let vb = self.f32_to_bf16(v, t_kv * n_head_kv * head_dim)?;
20077 let __s_b = self.gpu.stream();
20078 let mut b = __s_b.launch_builder(&f);
20079 b.arg(&qb)
20080 .arg(&kb)
20081 .arg(&vb)
20082 .arg(o)
20083 .arg(&hd)
20084 .arg(&nh)
20085 .arg(&nhkv)
20086 .arg(&ti)
20087 .arg(&tkvi)
20088 .arg(&scale)
20089 .arg(&cz);
20090 unsafe {
20091 b.launch(cfg)?;
20092 }
20093 }
20094 Ok(())
20095 }
20096
20097 #[allow(clippy::too_many_arguments)]
20101 pub fn rope_neox2_bf16e(
20102 &self,
20103 q: &mut CudaSlice<f32>,
20104 k: &mut CudaSlice<f32>,
20105 qb: &mut CudaSlice<u8>,
20106 kb: &mut CudaSlice<u8>,
20107 pos: &CudaSlice<i32>,
20108 head_dim: usize,
20109 n_dims: usize,
20110 nh_q: usize,
20111 nh_k: usize,
20112 n_tokens: usize,
20113 base: f32,
20114 freq_scale: f32,
20115 ff: Option<&CudaSlice<f32>>,
20116 ) -> Result<(), Box<dyn std::error::Error>> {
20117 let f = self.func("rope_neox2_bf16e_f32");
20118 let rows = ((nh_q + nh_k) * n_tokens) as u32;
20119 let cfg = LaunchConfig {
20120 grid_dim: (rows, 1, 1),
20121 block_dim: ((head_dim / 2) as u32, 1, 1),
20122 shared_mem_bytes: 0,
20123 };
20124 let theta_scale = base.powf(-2.0 / n_dims as f32);
20125 let (hd, nd, nhq, nhk, nt) = (
20126 head_dim as i32,
20127 n_dims as i32,
20128 nh_q as i32,
20129 nh_k as i32,
20130 n_tokens as i32,
20131 );
20132 let __s_b = self.gpu.stream();
20133 let mut b = __s_b.launch_builder(&f);
20134 match ff {
20135 Some(t) => {
20136 b.arg(&mut *q)
20137 .arg(&mut *k)
20138 .arg(&mut *qb)
20139 .arg(&mut *kb)
20140 .arg(pos)
20141 .arg(&hd)
20142 .arg(&nd)
20143 .arg(&nhq)
20144 .arg(&nhk)
20145 .arg(&nt)
20146 .arg(&theta_scale)
20147 .arg(&freq_scale)
20148 .arg(t);
20149 unsafe {
20150 b.launch(cfg)?;
20151 }
20152 }
20153 None => {
20154 let null: u64 = 0;
20155 b.arg(&mut *q)
20156 .arg(&mut *k)
20157 .arg(&mut *qb)
20158 .arg(&mut *kb)
20159 .arg(pos)
20160 .arg(&hd)
20161 .arg(&nd)
20162 .arg(&nhq)
20163 .arg(&nhk)
20164 .arg(&nt)
20165 .arg(&theta_scale)
20166 .arg(&freq_scale)
20167 .arg(&null);
20168 unsafe {
20169 b.launch(cfg)?;
20170 }
20171 }
20172 }
20173 Ok(())
20174 }
20175
20176 pub fn f32_to_bf16(
20179 &self,
20180 x: &CudaSlice<f32>,
20181 n: usize,
20182 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
20183 assert!(n % 4 == 0, "f32_to_bf16 requires n % 4 == 0, got {n}");
20184 let mut y = self.alloc_uninit::<u8>(n * 2)?;
20185 let f = self.func("f32_to_bf16_flat");
20186 let n_i = n as i64;
20187 let cfg = LaunchConfig {
20188 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
20189 block_dim: (256, 1, 1),
20190 shared_mem_bytes: 0,
20191 };
20192 let __s_b = self.gpu.stream();
20193 let mut b = __s_b.launch_builder(&f);
20194 b.arg(x).arg(&mut y).arg(&n_i);
20195 unsafe {
20196 b.launch(cfg)?;
20197 }
20198 Ok(y)
20199 }
20200
20201 pub fn f32_to_f16(
20202 &self,
20203 x: &CudaSlice<f32>,
20204 n: usize,
20205 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
20206 assert!(n % 4 == 0, "f32_to_f16 requires n % 4 == 0, got {n}");
20207 let mut y = self.alloc_uninit::<u8>(n * 2)?;
20208 let f = self.func("f32_to_f16_flat");
20209 let n_i = n as i64;
20210 let cfg = LaunchConfig {
20211 grid_dim: (((n / 4) as u32).div_ceil(256), 1, 1),
20212 block_dim: (256, 1, 1),
20213 shared_mem_bytes: 0,
20214 };
20215 let __s_b = self.gpu.stream();
20216 let mut b = __s_b.launch_builder(&f);
20217 b.arg(x).arg(&mut y).arg(&n_i);
20218 unsafe {
20219 b.launch(cfg)?;
20220 }
20221 Ok(y)
20222 }
20223
20224 pub fn bf16_to_f16(
20226 &self,
20227 xb: &CudaSlice<u8>,
20228 n: usize,
20229 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
20230 let mut y = self.alloc_uninit::<u8>(n * 2)?;
20231 self.bf16_to_f16_into(xb, n, &mut y)?;
20232 Ok(y)
20233 }
20234
20235 pub fn bf16_to_f16_into(
20237 &self,
20238 xb: &CudaSlice<u8>,
20239 n: usize,
20240 y: &mut CudaSlice<u8>,
20241 ) -> Result<(), Box<dyn std::error::Error>> {
20242 assert!(n % 2 == 0, "bf16_to_f16 requires n % 2 == 0, got {n}");
20243 assert!(y.len() >= n * 2);
20244 let f = self.func("bf16_to_f16_flat");
20245 let n2 = (n / 2) as i64;
20246 let cfg = LaunchConfig {
20247 grid_dim: (((n / 2) as u32).div_ceil(256), 1, 1),
20248 block_dim: (256, 1, 1),
20249 shared_mem_bytes: 0,
20250 };
20251 let __s_b = self.gpu.stream();
20252 let mut b = __s_b.launch_builder(&f);
20253 b.arg(xb).arg(y).arg(&n2);
20254 unsafe {
20255 b.launch(cfg)?;
20256 }
20257 Ok(())
20258 }
20259
20260 #[allow(clippy::too_many_arguments)]
20265 pub fn fa_prefill_vl8(
20266 &self,
20267 seqs: &[FaSeqVl],
20268 head_dim: usize,
20269 n_head: usize,
20270 n_head_kv: usize,
20271 scale: f32,
20272 ) -> Result<(), Box<dyn std::error::Error>> {
20273 const BK: usize = 32;
20274 let b = seqs.len();
20275 assert!(b >= 1 && b <= 8);
20276 let mut packed = [FaSeqVl::default(); 8];
20277 packed[..b].copy_from_slice(seqs);
20278 let v = FaVl8(packed);
20279 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20280 let ept = (n_head_kv * head_dim) as i32;
20281 {
20282 let f = self.func("fa_mirror_vl");
20283 let max_n = (max_t as i64) * ept as i64;
20284 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
20285 for which in 0..2i32 {
20286 let cfg = LaunchConfig {
20287 grid_dim: (blocks, 1, b as u32),
20288 block_dim: (256, 1, 1),
20289 shared_mem_bytes: 0,
20290 };
20291 let __s_lb = self.gpu.stream();
20292 let mut lb = __s_lb.launch_builder(&f);
20293 lb.arg(&v).arg(&ept).arg(&which);
20294 unsafe {
20295 lb.launch(cfg)?;
20296 }
20297 }
20298 }
20299 let hd_sfx = fa_hd_suffix(head_dim)?;
20300 let f = self.func(&format!("fa_prefill_bf16kv_vl{hd_sfx}"));
20301 let block_q = 64usize;
20302 let kv_stages = 2usize;
20303 let shmem = (2 * (kv_stages * 2 * BK * head_dim + block_q * BK)
20304 + 4 * (block_q * BK + 2 * block_q)) as u32;
20305 use cudarc::driver::sys::CUfunction_attribute_enum as A;
20306 f.set_attribute(
20307 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20308 shmem as i32,
20309 )?;
20310 let cfg = LaunchConfig {
20311 grid_dim: (max_t.div_ceil(block_q as u32), n_head as u32, b as u32),
20312 block_dim: (32, 4, 1),
20313 shared_mem_bytes: shmem,
20314 };
20315 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
20316 let __s_lb = self.gpu.stream();
20317 let mut lb = __s_lb.launch_builder(&f);
20318 lb.arg(&v).arg(&hd).arg(&nh).arg(&nhkv).arg(&scale);
20319 unsafe {
20320 lb.launch(cfg)?;
20321 }
20322 Ok(())
20323 }
20324
20325 #[allow(clippy::too_many_arguments)]
20329 pub fn attn_pre_vl8(
20330 &self,
20331 seqs: &[AttnPreVl],
20332 wq: &CudaSlice<f32>,
20333 wk: &CudaSlice<f32>,
20334 head_dim: usize,
20335 rope_dims: usize,
20336 n_head: usize,
20337 n_head_kv: usize,
20338 eps: f32,
20339 freq_base: f32,
20340 freq_scale: f32,
20341 kv_dim_k: usize,
20342 kv_dim_v: usize,
20343 k_tok_bytes: usize,
20344 v_tok_bytes: usize,
20345 ) -> Result<(), Box<dyn std::error::Error>> {
20346 let b = seqs.len();
20347 assert!(b >= 1 && b <= 8);
20348 let mut packed = [AttnPreVl::default(); 8];
20349 packed[..b].copy_from_slice(seqs);
20350 let v = AttnPreVl8(packed);
20351 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
20352 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
20353 {
20354 let f = self.func("q_gate_split_vl");
20355 let n = max_t * (n_head * head_dim) as u32;
20356 let cfg = LaunchConfig {
20357 grid_dim: (n.div_ceil(256), 1, b as u32),
20358 block_dim: (256, 1, 1),
20359 shared_mem_bytes: 0,
20360 };
20361 let __s_lb = self.gpu.stream();
20362 let mut lb = __s_lb.launch_builder(&f);
20363 lb.arg(&v).arg(&hd).arg(&nh);
20364 unsafe {
20365 lb.launch(cfg)?;
20366 }
20367 }
20368 {
20369 let f = self.func("attn_rms_vl");
20370 let cfg = LaunchConfig {
20371 grid_dim: (max_t * n_head as u32, 2, b as u32),
20372 block_dim: (rms_block(), 1, 1),
20373 shared_mem_bytes: 0,
20374 };
20375 let __s_lb = self.gpu.stream();
20376 let mut lb = __s_lb.launch_builder(&f);
20377 lb.arg(&v)
20378 .arg(wq)
20379 .arg(wk)
20380 .arg(&hd)
20381 .arg(&nh)
20382 .arg(&nhkv)
20383 .arg(&eps);
20384 unsafe {
20385 lb.launch(cfg)?;
20386 }
20387 }
20388 {
20389 let f = self.func("attn_rope_vl");
20390 let theta_scale = freq_base.powf(-2.0 / rope_dims as f32);
20391 let nd = rope_dims as i32;
20392 let cfg = LaunchConfig {
20393 grid_dim: (max_t * n_head as u32, 2, b as u32),
20394 block_dim: ((head_dim / 2) as u32, 1, 1),
20395 shared_mem_bytes: 0,
20396 };
20397 let __s_lb = self.gpu.stream();
20398 let mut lb = __s_lb.launch_builder(&f);
20399 lb.arg(&v)
20400 .arg(&hd)
20401 .arg(&nd)
20402 .arg(&nh)
20403 .arg(&nhkv)
20404 .arg(&theta_scale)
20405 .arg(&freq_scale);
20406 unsafe {
20407 lb.launch(cfg)?;
20408 }
20409 }
20410 {
20411 let f = self.func("append_kv_vl");
20412 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
20413 let cfg = LaunchConfig {
20414 grid_dim: (nblk, max_t, b as u32),
20415 block_dim: (32, 1, 1),
20416 shared_mem_bytes: 0,
20417 };
20418 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
20419 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
20420 let __s_lb = self.gpu.stream();
20421 let mut lb = __s_lb.launch_builder(&f);
20422 lb.arg(&v).arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
20423 unsafe {
20424 lb.launch(cfg)?;
20425 }
20426 }
20427 Ok(())
20428 }
20429
20430 pub fn fa_prefill_view(
20435 &self,
20436 q: &CudaSlice<f32>,
20437 k: &cudarc::driver::CudaView<u8>,
20438 v: &cudarc::driver::CudaView<u8>,
20439 o: &mut CudaSlice<f32>,
20440 head_dim: usize,
20441 n_head: usize,
20442 n_head_kv: usize,
20443 t: usize,
20444 t_kv: usize,
20445 scale: f32,
20446 causal: bool,
20447 k_tok_bytes: usize,
20448 v_tok_bytes: usize,
20449 g: bool,
20450 ) -> Result<(), Box<dyn std::error::Error>> {
20451 if portable_mma_gated() {
20452 return self.sdpa_naive_quantized_view(
20453 q,
20454 k,
20455 v,
20456 o,
20457 head_dim,
20458 n_head,
20459 n_head_kv,
20460 t,
20461 t_kv,
20462 scale,
20463 causal,
20464 k_tok_bytes,
20465 v_tok_bytes,
20466 );
20467 }
20468 const BLOCK_Q: usize = 64;
20469 const BK: usize = 32;
20470 let name = format!("fa_prefill_q{}", fa_hd_suffix(head_dim)?);
20473 let f = if g {
20474 self.func_g(&name)
20475 } else {
20476 self.func(&name)
20477 };
20478 let shmem =
20479 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32;
20480 use cudarc::driver::sys::CUfunction_attribute_enum as A;
20481 f.set_attribute(
20482 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20483 shmem as i32,
20484 )?;
20485 let cfg = LaunchConfig {
20486 grid_dim: (
20487 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
20488 n_head as u32,
20489 1,
20490 ),
20491 block_dim: (32, 4, 1),
20492 shared_mem_bytes: shmem,
20493 };
20494 let (hd, nh, nhkv, ti, tkvi, cz) = (
20495 head_dim as i32,
20496 n_head as i32,
20497 n_head_kv as i32,
20498 t as i32,
20499 t_kv as i32,
20500 causal as i32,
20501 );
20502 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
20503 let __s_b = self.gpu.stream();
20504 let mut b = __s_b.launch_builder(&f);
20505 b.arg(q)
20506 .arg(k)
20507 .arg(v)
20508 .arg(o)
20509 .arg(&hd)
20510 .arg(&nh)
20511 .arg(&nhkv)
20512 .arg(&ti)
20513 .arg(&tkvi)
20514 .arg(&scale)
20515 .arg(&cz)
20516 .arg(&ktb)
20517 .arg(&vtb);
20518 unsafe {
20519 b.launch(cfg)?;
20520 }
20521 Ok(())
20522 }
20523
20524 #[allow(clippy::too_many_arguments)]
20534 pub fn fa_prefill_view_ws(
20535 &self,
20536 q: &CudaSlice<f32>,
20537 k: &cudarc::driver::CudaView<u8>,
20538 v: &cudarc::driver::CudaView<u8>,
20539 o: &mut CudaSlice<f32>,
20540 head_dim: usize,
20541 n_head: usize,
20542 n_head_kv: usize,
20543 t: usize,
20544 t_kv: usize,
20545 scale: f32,
20546 causal: bool,
20547 k_tok_bytes: usize,
20548 v_tok_bytes: usize,
20549 g: bool,
20550 ) -> Result<(), Box<dyn std::error::Error>> {
20551 if portable_mma_gated() {
20552 return self.sdpa_naive_quantized_view(
20553 q,
20554 k,
20555 v,
20556 o,
20557 head_dim,
20558 n_head,
20559 n_head_kv,
20560 t,
20561 t_kv,
20562 scale,
20563 causal,
20564 k_tok_bytes,
20565 v_tok_bytes,
20566 );
20567 }
20568 const BLOCK_Q: usize = 64;
20569 const BK: usize = 32;
20570 let kv_dim_k = n_head_kv * head_dim;
20571 let kv_dim_v = n_head_kv * head_dim;
20572 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
20574 let mut guard = self.prime_deqw_ws.lock().unwrap();
20576 let need_grow = match guard.as_ref() {
20577 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
20578 None => true,
20579 };
20580 if need_grow {
20581 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
20582 let (ck, cv) = guard
20583 .as_ref()
20584 .map(|(a, b)| (a.len(), b.len()))
20585 .unwrap_or((0, 0));
20586 *guard = Some((
20587 self.alloc_u8(grow(ck, k_ws_bytes))?,
20588 self.alloc_u8(grow(cv, v_ws_bytes))?,
20589 ));
20590 }
20591 let (kw, vw) = guard.as_mut().unwrap();
20592 {
20594 let f = if g {
20596 self.func_g("fa_dequant_kv_ws_bf16")
20597 } else {
20598 self.func("fa_dequant_kv_ws_bf16")
20599 };
20600 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
20601 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
20602 let cfg = LaunchConfig {
20603 grid_dim: (nblk.max(1), 1, 1),
20604 block_dim: (256, 1, 1),
20605 shared_mem_bytes: 0,
20606 };
20607 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
20608 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
20609 let __s_b = self.gpu.stream();
20610 let mut b = __s_b.launch_builder(&f);
20611 b.arg(k)
20612 .arg(v)
20613 .arg(&mut *kw)
20614 .arg(&mut *vw)
20615 .arg(&kdk)
20616 .arg(&kdv)
20617 .arg(&tkvi)
20618 .arg(&ktb)
20619 .arg(&vtb);
20620 unsafe {
20621 b.launch(cfg)?;
20622 }
20623 }
20624 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
20632 .map(|v| v != "0")
20633 .unwrap_or(true);
20634 {
20635 let hd_sfx = fa_hd_suffix(head_dim)?;
20636 let f = self.func(&format!(
20637 "fa_prefill_qw{}{hd_sfx}",
20638 if db { "_db" } else { "" }
20639 ));
20640 let shmem = if db {
20641 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
20643 } else {
20644 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
20645 };
20646 use cudarc::driver::sys::CUfunction_attribute_enum as A;
20647 f.set_attribute(
20648 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20649 shmem as i32,
20650 )?;
20651 let cfg = LaunchConfig {
20652 grid_dim: (
20653 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
20654 n_head as u32,
20655 1,
20656 ),
20657 block_dim: (32, 4, 1),
20658 shared_mem_bytes: shmem,
20659 };
20660 let (hd, nh, nhkv, ti, tkvi, cz) = (
20661 head_dim as i32,
20662 n_head as i32,
20663 n_head_kv as i32,
20664 t as i32,
20665 t_kv as i32,
20666 causal as i32,
20667 );
20668 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
20669 let __s_b = self.gpu.stream();
20670 let mut b = __s_b.launch_builder(&f);
20671 b.arg(q)
20672 .arg(&*kw)
20673 .arg(&*vw)
20674 .arg(o)
20675 .arg(&hd)
20676 .arg(&nh)
20677 .arg(&nhkv)
20678 .arg(&ti)
20679 .arg(&tkvi)
20680 .arg(&scale)
20681 .arg(&cz)
20682 .arg(&kdk)
20683 .arg(&kdv);
20684 unsafe {
20685 b.launch(cfg)?;
20686 }
20687 }
20688 Ok(())
20689 }
20690
20691 #[allow(clippy::too_many_arguments)]
20707 pub fn fa_prefill_view_ws_w_hd128(
20708 &self,
20709 q: &CudaSlice<f32>,
20710 k: &cudarc::driver::CudaView<u8>,
20711 v: &cudarc::driver::CudaView<u8>,
20712 o: &mut CudaSlice<f32>,
20713 head_dim: usize,
20714 n_head: usize,
20715 n_head_kv: usize,
20716 t: usize,
20717 t_kv: usize,
20718 scale: f32,
20719 causal: bool,
20720 window: usize,
20721 k_tok_bytes: usize,
20722 v_tok_bytes: usize,
20723 ) -> Result<(), Box<dyn std::error::Error>> {
20724 assert_eq!(
20725 head_dim, 128,
20726 "fa_prefill_view_ws_w_hd128: only the hd128 twin is stamped"
20727 );
20728 if portable_mma_gated() {
20729 return self.sdpa_naive_w_quantized_view(
20730 q,
20731 k,
20732 v,
20733 o,
20734 head_dim,
20735 n_head,
20736 n_head_kv,
20737 t,
20738 t_kv,
20739 scale,
20740 causal,
20741 window,
20742 k_tok_bytes,
20743 v_tok_bytes,
20744 );
20745 }
20746 const BLOCK_Q: usize = 64;
20747 const BK: usize = 32;
20748 let kv_dim_k = n_head_kv * head_dim;
20749 let kv_dim_v = n_head_kv * head_dim;
20750 let k_ws_bytes = t_kv * kv_dim_k * 2; let v_ws_bytes = t_kv * kv_dim_v * 2;
20752 let mut guard = self.prime_deqw_ws.lock().unwrap();
20753 let need_grow = match guard.as_ref() {
20754 Some((kw, vw)) => kw.len() < k_ws_bytes || vw.len() < v_ws_bytes,
20755 None => true,
20756 };
20757 if need_grow {
20758 let grow = |cur: usize, need: usize| if cur >= need { cur } else { need };
20759 let (ck, cv) = guard
20760 .as_ref()
20761 .map(|(a, b)| (a.len(), b.len()))
20762 .unwrap_or((0, 0));
20763 *guard = Some((
20764 self.alloc_u8(grow(ck, k_ws_bytes))?,
20765 self.alloc_u8(grow(cv, v_ws_bytes))?,
20766 ));
20767 }
20768 let (kw, vw) = guard.as_mut().unwrap();
20769 {
20772 let f = self.func("fa_dequant_kv_ws_bf16");
20773 let total = (t_kv * (kv_dim_k + kv_dim_v)) as u64;
20774 let nblk = ((total + 255) / 256).min(65535 * 16) as u32;
20775 let cfg = LaunchConfig {
20776 grid_dim: (nblk.max(1), 1, 1),
20777 block_dim: (256, 1, 1),
20778 shared_mem_bytes: 0,
20779 };
20780 let (kdk, kdv, tkvi) = (kv_dim_k as i32, kv_dim_v as i32, t_kv as i32);
20781 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
20782 let __s_b = self.gpu.stream();
20783 let mut b = __s_b.launch_builder(&f);
20784 b.arg(k)
20785 .arg(v)
20786 .arg(&mut *kw)
20787 .arg(&mut *vw)
20788 .arg(&kdk)
20789 .arg(&kdv)
20790 .arg(&tkvi)
20791 .arg(&ktb)
20792 .arg(&vtb);
20793 unsafe {
20794 b.launch(cfg)?;
20795 }
20796 }
20797 let db = std::env::var("MEMRA_PRIME_DEQW_DB")
20799 .map(|v| v != "0")
20800 .unwrap_or(true);
20801 {
20802 let f = self.func(if db {
20803 "fa_prefill_qw_db_w_hd128"
20804 } else {
20805 "fa_prefill_qw_w_hd128"
20806 });
20807 let shmem = if db {
20808 (2 * (4 * BK * head_dim + BLOCK_Q * BK) + 4 * BLOCK_Q) as u32
20809 } else {
20810 (2 * (2 * BK * head_dim + BLOCK_Q * BK) + 4 * (BLOCK_Q * BK + 2 * BLOCK_Q)) as u32
20811 };
20812 use cudarc::driver::sys::CUfunction_attribute_enum as A;
20813 f.set_attribute(
20814 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
20815 shmem as i32,
20816 )?;
20817 let cfg = LaunchConfig {
20818 grid_dim: (
20819 (t as u32 + BLOCK_Q as u32 - 1) / BLOCK_Q as u32,
20820 n_head as u32,
20821 1,
20822 ),
20823 block_dim: (32, 4, 1),
20824 shared_mem_bytes: shmem,
20825 };
20826 let (hd, nh, nhkv, ti, tkvi, cz) = (
20827 head_dim as i32,
20828 n_head as i32,
20829 n_head_kv as i32,
20830 t as i32,
20831 t_kv as i32,
20832 causal as i32,
20833 );
20834 let (kdk, kdv, wnd) = (kv_dim_k as i32, kv_dim_v as i32, window as i32);
20835 let __s_b = self.gpu.stream();
20836 let mut b = __s_b.launch_builder(&f);
20837 b.arg(q)
20838 .arg(&*kw)
20839 .arg(&*vw)
20840 .arg(o)
20841 .arg(&hd)
20842 .arg(&nh)
20843 .arg(&nhkv)
20844 .arg(&ti)
20845 .arg(&tkvi)
20846 .arg(&scale)
20847 .arg(&cz)
20848 .arg(&kdk)
20849 .arg(&kdv)
20850 .arg(&wnd);
20851 unsafe {
20852 b.launch(cfg)?;
20853 }
20854 }
20855 Ok(())
20856 }
20857
20858 pub fn fa_decode(
20862 &self,
20863 q: &CudaSlice<f32>,
20864 k: &cudarc::driver::CudaView<u8>,
20865 v: &cudarc::driver::CudaView<u8>,
20866 o: &mut CudaSlice<f32>,
20867 head_dim: usize,
20868 n_head: usize,
20869 n_head_kv: usize,
20870 t_kv: usize,
20871 scale: f32,
20872 k_tok_bytes: usize,
20873 v_tok_bytes: usize,
20874 ) -> Result<(), Box<dyn std::error::Error>> {
20875 self.fa_decode_kvmod(
20876 q,
20877 k,
20878 v,
20879 o,
20880 head_dim,
20881 n_head,
20882 n_head_kv,
20883 t_kv,
20884 scale,
20885 k_tok_bytes,
20886 v_tok_bytes,
20887 false,
20888 )
20889 }
20890
20891 #[allow(clippy::too_many_arguments)]
20895 #[allow(clippy::too_many_arguments)]
20899 #[allow(clippy::too_many_arguments)]
20900 fn fa_decode_scalar_unified(
20901 &self,
20902 q: &cudarc::driver::CudaView<f32>,
20903 k: &cudarc::driver::CudaView<u8>,
20904 v: &cudarc::driver::CudaView<u8>,
20905 o: &mut cudarc::driver::CudaViewMut<f32>,
20906 head_dim: usize,
20907 n_head: usize,
20908 n_head_kv: usize,
20909 t_kv_host: usize,
20910 t_kv_dev: Option<&CudaSlice<i32>>,
20911 scale: f32,
20912 n_splits: usize,
20913 split_keys: usize,
20914 k_tok_bytes: usize,
20915 v_tok_bytes: usize,
20916 g: bool,
20917 part_o: &mut CudaSlice<f32>,
20918 part_m: &mut CudaSlice<f32>,
20919 part_l: &mut CudaSlice<f32>,
20920 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
20921 ) -> Result<(), Box<dyn std::error::Error>> {
20922 let f = if g {
20923 self.func_g("fa_decode_f32")
20924 } else {
20925 self.fa_func("fa_decode_f32", head_dim)
20926 };
20927 let cfg = LaunchConfig {
20928 grid_dim: (n_head as u32, n_splits as u32, 1),
20929 block_dim: (head_dim as u32, 1, 1),
20930 shared_mem_bytes: (4 * (head_dim + 32)) as u32,
20931 };
20932 let (hd, nh, nhkv, nsp) = (
20933 head_dim as i32,
20934 n_head as i32,
20935 n_head_kv as i32,
20936 n_splits as i32,
20937 );
20938 let (ktb, vtb, tkvi, ski) = (
20939 k_tok_bytes as i64,
20940 v_tok_bytes as i64,
20941 t_kv_host as i32,
20942 split_keys as i32,
20943 );
20944 let __s_b = self.gpu.stream();
20945 let mut b = __s_b.launch_builder(&f);
20946 match t_kv_dev {
20947 Some(d) => {
20948 b.arg(q)
20949 .arg(k)
20950 .arg(v)
20951 .arg(&mut *part_o)
20952 .arg(&mut *part_m)
20953 .arg(&mut *part_l)
20954 .arg(&hd)
20955 .arg(&nh)
20956 .arg(&nhkv)
20957 .arg(&tkvi)
20958 .arg(d)
20959 .arg(&scale)
20960 .arg(&nsp)
20961 .arg(&ski)
20962 .arg(&ktb)
20963 .arg(&vtb);
20964 unsafe {
20965 b.launch(cfg)?;
20966 }
20967 }
20968 None => {
20969 let null: u64 = 0;
20970 b.arg(q)
20971 .arg(k)
20972 .arg(v)
20973 .arg(&mut *part_o)
20974 .arg(&mut *part_m)
20975 .arg(&mut *part_l)
20976 .arg(&hd)
20977 .arg(&nh)
20978 .arg(&nhkv)
20979 .arg(&tkvi)
20980 .arg(&null)
20981 .arg(&scale)
20982 .arg(&nsp)
20983 .arg(&ski)
20984 .arg(&ktb)
20985 .arg(&vtb);
20986 unsafe {
20987 b.launch(cfg)?;
20988 }
20989 }
20990 }
20991 let cfg2 = LaunchConfig {
20992 grid_dim: (n_head as u32, 1, 1),
20993 block_dim: (head_dim as u32, 1, 1),
20994 shared_mem_bytes: 0,
20995 };
20996 if let Some((oq, od)) = q8_out {
20997 let fc = if g {
20999 self.func_g("fa_decode_combine_q8_1")
21000 } else {
21001 self.fa_func("fa_decode_combine_q8_1", head_dim)
21002 };
21003 let __s_b2 = self.gpu.stream();
21004 let mut b2 = __s_b2.launch_builder(&fc);
21005 b2.arg(&*part_o)
21006 .arg(&*part_m)
21007 .arg(&*part_l)
21008 .arg(oq)
21009 .arg(od)
21010 .arg(&hd)
21011 .arg(&nh)
21012 .arg(&nsp);
21013 unsafe {
21014 b2.launch(cfg2)?;
21015 }
21016 return Ok(());
21017 }
21018 let fc = if g {
21019 self.func_g("fa_decode_combine_f32")
21020 } else {
21021 self.fa_func("fa_decode_combine_f32", head_dim)
21022 };
21023 let __s_b2 = self.gpu.stream();
21024 let mut b2 = __s_b2.launch_builder(&fc);
21025 b2.arg(&*part_o)
21026 .arg(&*part_m)
21027 .arg(&*part_l)
21028 .arg(o)
21029 .arg(&hd)
21030 .arg(&nh)
21031 .arg(&nsp);
21032 unsafe {
21033 b2.launch(cfg2)?;
21034 }
21035 Ok(())
21036 }
21037
21038 pub fn fa_decode_kvmod(
21039 &self,
21040 q: &CudaSlice<f32>,
21041 k: &cudarc::driver::CudaView<u8>,
21042 v: &cudarc::driver::CudaView<u8>,
21043 o: &mut CudaSlice<f32>,
21044 head_dim: usize,
21045 n_head: usize,
21046 n_head_kv: usize,
21047 t_kv: usize,
21048 scale: f32,
21049 k_tok_bytes: usize,
21050 v_tok_bytes: usize,
21051 g: bool,
21052 ) -> Result<(), Box<dyn std::error::Error>> {
21053 let q_view = q.as_view();
21054 let mut o_view = o.as_view_mut();
21055 self.fa_decode_kvmod_view(
21056 &q_view,
21057 k,
21058 v,
21059 &mut o_view,
21060 head_dim,
21061 n_head,
21062 n_head_kv,
21063 t_kv,
21064 scale,
21065 k_tok_bytes,
21066 v_tok_bytes,
21067 g,
21068 )
21069 }
21070
21071 #[allow(clippy::too_many_arguments)]
21076 pub fn fa_decode_kvmod_view(
21077 &self,
21078 q: &cudarc::driver::CudaView<f32>,
21079 k: &cudarc::driver::CudaView<u8>,
21080 v: &cudarc::driver::CudaView<u8>,
21081 o: &mut cudarc::driver::CudaViewMut<f32>,
21082 head_dim: usize,
21083 n_head: usize,
21084 n_head_kv: usize,
21085 t_kv: usize,
21086 scale: f32,
21087 k_tok_bytes: usize,
21088 v_tok_bytes: usize,
21089 g: bool,
21090 ) -> Result<(), Box<dyn std::error::Error>> {
21091 let mut fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
21112 if g && head_dim == 256 && !fa_v4_at(t_kv) {
21116 fa_vec = false;
21117 }
21118 let sp = fa_split_keys(t_kv, n_head_kv);
21119 let n_splits = if fa_vec {
21120 ((t_kv + sp - 1) / sp).max(1)
21121 } else {
21122 ((t_kv + 255) / 256).max(1)
21123 };
21124 let o_len = n_head * n_splits * head_dim;
21125 let ml_len = n_head * n_splits;
21126 let mut part_guard = self.fa_part_pool.lock().unwrap();
21127 if part_guard
21128 .as_ref()
21129 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
21130 .unwrap_or(true)
21131 {
21132 let old = part_guard.take();
21143 let (co, cm) = old
21144 .as_ref()
21145 .map(|pp| (pp.0.len(), pp.1.len()))
21146 .unwrap_or((0, 0));
21147 if let Some(old) = old {
21148 self.fa_part_retired.lock().unwrap().push(old);
21149 }
21150 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
21151 eprintln!(
21152 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
21153 co, o_len, cm, ml_len
21154 );
21155 }
21156 *part_guard = Some((
21157 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
21158 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
21159 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
21160 ));
21161 }
21162 let pg = part_guard.as_mut().unwrap();
21163 self.gpu
21164 .stream()
21165 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
21166 self.gpu
21167 .stream()
21168 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
21169 self.gpu
21170 .stream()
21171 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
21172 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
21173 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
21174 let (hd, nh, nhkv, tkvi, nsp) = (
21175 head_dim as i32,
21176 n_head as i32,
21177 n_head_kv as i32,
21178 t_kv as i32,
21179 n_splits as i32,
21180 );
21181 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
21182 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
21186 let fa512_min = fa512_min_tkv();
21191 let deep = fa_vec
21194 && head_dim == 256
21195 && fa_v4_at(t_kv)
21196 && !g
21197 && fa_deep_at(t_kv)
21198 && !matches!(fa_v4_mode(), "noB3" | "stage");
21199 let (f, cfg) = if fa_vec && head_dim == 512 && t_kv >= fa512_min {
21200 let gqa = (n_head / n_head_kv).max(1) as u32;
21203 let fv = self.fa_func("fa_decode_vec_q_dpl16", head_dim);
21204 (
21205 fv,
21206 LaunchConfig {
21207 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
21208 block_dim: (32, gqa, 1),
21209 shared_mem_bytes: 0,
21210 },
21211 )
21212 } else if fa_vec && head_dim <= 256 {
21213 let gqa = (n_head / n_head_kv).max(1) as u32;
21214 static SMEM_TKV: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
21225 let smem_tkv = *SMEM_TKV.get_or_init(|| {
21226 std::env::var("MEMRA_FA_SMEM_TKV")
21227 .ok()
21228 .and_then(|v| v.parse().ok())
21229 .unwrap_or_else(|| {
21230 FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
21231 })
21232 });
21233 if fa_v4_at(t_kv) && head_dim == 256 {
21234 let v4name = match fa_v4_mode() {
21238 "noB3" => "fa_decode_vec_q_v4_noB3", "stage" => "fa_decode_vec_q_v4_stage", _ if deep => "fa_decode_vec_q_v4_deep",
21241 _ => "fa_decode_vec_q_v4",
21242 };
21243 let fv = if g {
21244 self.func_g(v4name)
21245 } else {
21246 self.func(v4name)
21247 };
21248 let shmem = (if deep { 12160 } else { 11520 }
21251 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
21252 use cudarc::driver::sys::CUfunction_attribute_enum as A;
21253 fv.set_attribute(
21254 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21255 shmem as i32,
21256 )?;
21257 (
21258 fv,
21259 LaunchConfig {
21260 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
21261 block_dim: (32, gqa, 1),
21262 shared_mem_bytes: shmem,
21263 },
21264 )
21265 } else if fa_v3_active(head_dim) {
21266 let fv = if g {
21269 self.func_g("fa_decode_vec_q_v3")
21270 } else {
21271 self.func("fa_decode_vec_q_v3")
21272 };
21273 let shmem = (32 * head_dim * 2) as u32; (
21275 fv,
21276 LaunchConfig {
21277 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
21278 block_dim: (32, gqa, 1),
21279 shared_mem_bytes: shmem,
21280 },
21281 )
21282 } else if fa_v2_on() {
21283 let fv = if g {
21287 self.func_g("fa_decode_vec_q_v2")
21288 } else {
21289 self.func("fa_decode_vec_q_v2")
21290 };
21291 let shmem = (2 * 32 * head_dim * 2) as u32; (
21293 fv,
21294 LaunchConfig {
21295 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
21296 block_dim: (32, gqa, 1),
21297 shared_mem_bytes: shmem,
21298 },
21299 )
21300 } else if smem_tkv > 0 && t_kv >= smem_tkv && !g && !(head_dim == 512 && Self::gkv_on())
21301 {
21302 let fv = if g {
21306 self.func_g("fa_decode_vec_q_smem")
21307 } else {
21308 self.func("fa_decode_vec_q_smem")
21309 };
21310 let shmem = (2 * 32 * head_dim * 2) as u32; use cudarc::driver::sys::CUfunction_attribute_enum as A;
21312 fv.set_attribute(
21313 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21314 shmem as i32,
21315 )?;
21316 (
21317 fv,
21318 LaunchConfig {
21319 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
21320 block_dim: (32, gqa, 1),
21321 shared_mem_bytes: shmem,
21322 },
21323 )
21324 } else {
21325 let fv = if g {
21328 self.func_g("fa_decode_vec_q")
21329 } else {
21330 self.func("fa_decode_vec_q")
21331 };
21332 (
21333 fv,
21334 LaunchConfig {
21335 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
21336 block_dim: (32, gqa, 1),
21337 shared_mem_bytes: 0,
21338 },
21339 )
21340 }
21341 } else {
21342 return self.fa_decode_scalar_unified(
21345 q,
21346 k,
21347 v,
21348 o,
21349 head_dim,
21350 n_head,
21351 n_head_kv,
21352 t_kv,
21353 None,
21354 scale,
21355 n_splits,
21356 if fa_vec { sp } else { 256 },
21357 k_tok_bytes,
21358 v_tok_bytes,
21359 g,
21360 part_o,
21361 part_m,
21362 part_l,
21363 None,
21364 );
21365 };
21366 let __s_b = self.gpu.stream();
21367 let mut b = __s_b.launch_builder(&f);
21368 b.arg(q)
21369 .arg(k)
21370 .arg(v)
21371 .arg(&mut *part_o)
21372 .arg(&mut *part_m)
21373 .arg(&mut *part_l)
21374 .arg(&hd)
21375 .arg(&nh)
21376 .arg(&nhkv)
21377 .arg(&tkvi)
21378 .arg(&scale)
21379 .arg(&nsp)
21380 .arg(&ktb)
21381 .arg(&vtb);
21382 unsafe {
21383 b.launch(cfg)?;
21384 }
21385 let (fc, cfg2) = (
21388 if g {
21389 self.func_g("fa_decode_combine_f32")
21390 } else {
21391 self.fa_func("fa_decode_combine_f32", head_dim)
21392 },
21393 LaunchConfig {
21394 grid_dim: (n_head as u32, 1, 1),
21395 block_dim: (head_dim as u32, 1, 1),
21396 shared_mem_bytes: 0,
21397 },
21398 );
21399 let __s_b2 = self.gpu.stream();
21400 let mut b2 = __s_b2.launch_builder(&fc);
21401 b2.arg(&*part_o)
21402 .arg(&*part_m)
21403 .arg(&*part_l)
21404 .arg(o)
21405 .arg(&hd)
21406 .arg(&nh)
21407 .arg(&nsp);
21408 unsafe {
21409 b2.launch(cfg2)?;
21410 }
21411 Ok(())
21412 }
21413
21414 #[allow(clippy::too_many_arguments)]
21425 pub fn fa_decode_batch_seqs_v4(
21426 &self,
21427 q: &CudaSlice<f32>,
21428 kv_ptrs: &cudarc::driver::CudaView<u64>,
21429 pos_seq: &CudaSlice<i32>,
21430 o: &mut CudaSlice<f32>,
21431 head_dim: usize,
21432 n_head: usize,
21433 n_head_kv: usize,
21434 b_n: usize,
21435 t_kv_max: usize,
21436 scale: f32,
21437 split_keys: usize,
21438 k_tok_bytes: usize,
21439 v_tok_bytes: usize,
21440 ) -> Result<(), Box<dyn std::error::Error>> {
21441 debug_assert!(head_dim == 256, "seqs twin is v4-stamped (hd256 only)");
21442 let n_splits_max = (t_kv_max + split_keys - 1) / split_keys;
21443 let o_len = b_n * n_head * n_splits_max * head_dim;
21444 let ml_len = b_n * n_head * n_splits_max;
21445 let mut part_guard = self.fa_part_pool.lock().unwrap();
21446 if part_guard
21447 .as_ref()
21448 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
21449 .unwrap_or(true)
21450 {
21451 let old = part_guard.take();
21462 let (co, cm) = old
21463 .as_ref()
21464 .map(|pp| (pp.0.len(), pp.1.len()))
21465 .unwrap_or((0, 0));
21466 if let Some(old) = old {
21467 self.fa_part_retired.lock().unwrap().push(old);
21468 }
21469 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
21470 eprintln!(
21471 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
21472 co, o_len, cm, ml_len
21473 );
21474 }
21475 *part_guard = Some((
21476 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
21477 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
21478 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
21479 ));
21480 }
21481 let pg = part_guard.as_mut().unwrap();
21482 self.gpu
21483 .stream()
21484 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
21485 self.gpu
21486 .stream()
21487 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
21488 self.gpu
21489 .stream()
21490 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
21491 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
21492 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
21493 let (nspm, spk) = (n_splits_max as i32, split_keys as i32);
21494 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
21495 let gqa = (n_head / n_head_kv).max(1) as u32;
21496 let f = self.func("fa_decode_vec_q_seqs_v4");
21497 let shmem = (11520 + 32 * head_dim * 2) as u32;
21499 use cudarc::driver::sys::CUfunction_attribute_enum as A;
21500 f.set_attribute(
21501 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21502 shmem as i32,
21503 )?;
21504 let cfg = LaunchConfig {
21505 grid_dim: (n_head_kv as u32, n_splits_max as u32, b_n as u32),
21506 block_dim: (32, gqa, 1),
21507 shared_mem_bytes: shmem,
21508 };
21509 {
21510 let __s_b = self.gpu.stream();
21511 let mut b = __s_b.launch_builder(&f);
21512 b.arg(q)
21513 .arg(kv_ptrs)
21514 .arg(pos_seq)
21515 .arg(&mut *part_o)
21516 .arg(&mut *part_m)
21517 .arg(&mut *part_l)
21518 .arg(&hd)
21519 .arg(&nh)
21520 .arg(&nhkv)
21521 .arg(&scale)
21522 .arg(&nspm)
21523 .arg(&spk)
21524 .arg(&ktb)
21525 .arg(&vtb);
21526 unsafe {
21527 b.launch(cfg)?;
21528 }
21529 }
21530 let fc = self.func("fa_decode_combine_seqs");
21531 let cfg2 = LaunchConfig {
21532 grid_dim: (n_head as u32, b_n as u32, 1),
21533 block_dim: (head_dim as u32, 1, 1),
21534 shared_mem_bytes: 0,
21535 };
21536 let __s_b2 = self.gpu.stream();
21537 let mut b2 = __s_b2.launch_builder(&fc);
21538 b2.arg(&*part_o)
21539 .arg(&*part_m)
21540 .arg(&*part_l)
21541 .arg(o)
21542 .arg(&hd)
21543 .arg(&nh)
21544 .arg(pos_seq)
21545 .arg(&nspm)
21546 .arg(&spk);
21547 unsafe {
21548 b2.launch(cfg2)?;
21549 }
21550 Ok(())
21551 }
21552
21553 #[allow(clippy::too_many_arguments)]
21560 pub fn append_kv_quantized_seqs(
21561 &self,
21562 k_rows: &CudaSlice<f32>,
21563 v_rows: &CudaSlice<f32>,
21564 kv_ptrs: &cudarc::driver::CudaView<u64>,
21565 pos_seq: &CudaSlice<i32>,
21566 b_n: usize,
21567 kv_dim_k: usize,
21568 kv_dim_v: usize,
21569 k_tok_bytes: usize,
21570 v_tok_bytes: usize,
21571 ) -> Result<(), Box<dyn std::error::Error>> {
21572 let f = self.func("append_quantize_kv_q8_0_q5_1_seqs");
21573 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
21574 let cfg = LaunchConfig {
21575 grid_dim: (nblk, b_n as u32, 1),
21576 block_dim: (32, 1, 1),
21577 shared_mem_bytes: 0,
21578 };
21579 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
21580 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
21581 let __s_b = self.gpu.stream();
21582 let mut b = __s_b.launch_builder(&f);
21583 b.arg(k_rows)
21584 .arg(v_rows)
21585 .arg(kv_ptrs)
21586 .arg(pos_seq)
21587 .arg(&kdk)
21588 .arg(&kdv)
21589 .arg(&ktb)
21590 .arg(&vtb);
21591 unsafe {
21592 b.launch(cfg)?;
21593 }
21594 Ok(())
21595 }
21596
21597 pub fn fa_rows_eligible(&self, base_len: usize, head_dim: usize) -> bool {
21603 std::env::var("MEMRA_NO_FA_VEC").is_err()
21604 && std::env::var("MEMRA_FA_ROWS_OFF").is_err()
21605 && base_len + 1 >= fa_vec_min_tkv()
21606 && head_dim <= 256
21607 && head_dim % 32 == 0
21608 }
21609
21610 #[allow(clippy::too_many_arguments)]
21619 pub fn fa_decode_rows(
21620 &self,
21621 q: &CudaSlice<f32>,
21622 k: &cudarc::driver::CudaView<u8>,
21623 v: &cudarc::driver::CudaView<u8>,
21624 o: &mut CudaSlice<f32>,
21625 head_dim: usize,
21626 n_head: usize,
21627 n_head_kv: usize,
21628 base_len: usize,
21629 t: usize,
21630 scale: f32,
21631 k_tok_bytes: usize,
21632 v_tok_bytes: usize,
21633 base_dev: Option<(&CudaSlice<i32>, i32)>,
21637 kv_shared: bool,
21640 g: bool,
21644 mut q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
21647 ) -> Result<(), Box<dyn std::error::Error>> {
21648 debug_assert!(base_len + 1 >= fa_vec_min_tkv() && head_dim <= 512 && head_dim % 32 == 0);
21649 let t_kv_max = base_len + t; let mut sp = fa_split_keys(t_kv_max, n_head_kv); if head_dim == 512 {
21656 static SP512: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
21657 let v = *SP512.get_or_init(|| {
21660 std::env::var("MEMRA_FA_SP512")
21661 .ok()
21662 .and_then(|x| x.parse().ok())
21663 .unwrap_or(0)
21664 });
21665 sp = if v >= 8 {
21666 v
21667 } else {
21668 FA_SP512_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
21669 };
21670 }
21671 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
21672 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
21673 let gqa = (n_head / n_head_kv).max(1) as u32;
21674 let mut groups: Vec<(usize, usize, usize)> = Vec::new(); if head_dim == 512 || fa_split_keys(base_len + 1, n_head_kv) == sp {
21685 groups.push((0, t, sp));
21686 } else {
21687 let mut r0 = 0usize;
21688 while r0 < t {
21689 let sp_g = fa_split_keys(base_len + r0 + 1, n_head_kv);
21690 let mut r1 = r0 + 1;
21691 while r1 < t && fa_split_keys(base_len + r1 + 1, n_head_kv) == sp_g {
21692 r1 += 1;
21693 }
21694 groups.push((r0, r1 - r0, sp_g));
21695 r0 = r1;
21696 }
21697 }
21698 static SMEM_TKV_R: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
21702 let smem_tkv = *SMEM_TKV_R.get_or_init(|| {
21703 std::env::var("MEMRA_FA_SMEM_TKV")
21704 .ok()
21705 .and_then(|v| v.parse().ok())
21706 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
21707 });
21708 let v4 = fa_v4_at(base_len + t) && head_dim == 256;
21709 let v3 = fa_v3_active(head_dim);
21710 let smem_rows =
21711 head_dim <= 256 && !v3 && !fa_v2_on() && smem_tkv > 0 && t_kv_max >= smem_tkv;
21712 let _ = kv_shared;
21717 let i2 = head_dim == 512 && std::env::var("MEMRA_FA_I2").as_deref() != Ok("0");
21720 static TB512: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
21734 let tb512 = head_dim == 512
21736 && sp <= 32
21737 && n_head / n_head_kv.max(1) <= 16
21738 && *TB512.get_or_init(|| std::env::var("MEMRA_FA_TB512").as_deref() != Ok("0"));
21739 let fname = if tb512 {
21740 "fa_decode_vec_q_rows_v4_512_tb"
21741 } else if i2 {
21742 "fa_decode_vec_q_rows_dpl16_i2"
21743 } else if head_dim == 512 {
21744 "fa_decode_vec_q_rows_dpl16"
21745 }
21746 else if v4 {
21748 "fa_decode_vec_q_rows_v4"
21749 } else if v3 {
21750 "fa_decode_vec_q_rows_v3"
21751 } else if fa_v2_on() {
21752 "fa_decode_vec_q_rows_v2"
21753 } else if smem_rows {
21754 "fa_decode_vec_q_rows_smem"
21755 } else {
21756 "fa_decode_vec_q_rows"
21757 };
21758 let f = if head_dim == 512 {
21759 self.fa_func(fname, head_dim)
21760 } else if g {
21761 self.func_g(if smem_rows {
21769 "fa_decode_vec_q_rows"
21770 } else {
21771 fname
21772 })
21773 } else {
21774 self.func(fname)
21775 };
21776 let shmem = if tb512 {
21777 let gk = Self::gkv_on();
21779 let sh =
21780 (8192 + 1024 + 32 * 512 + 32 * 64 + 32 * head_dim * if gk { 1 } else { 2 }) as u32;
21781 use cudarc::driver::sys::CUfunction_attribute_enum as A;
21782 f.set_attribute(
21783 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21784 sh as i32,
21785 )?;
21786 sh
21787 } else if v4 || v3 || smem_rows || fa_v2_on() {
21788 let sh = (if v4 {
21790 11520 + 32 * head_dim * if g { 1 } else { 2 }
21791 } else if v3 {
21792 32 * head_dim * 2
21793 } else {
21794 2 * 32 * head_dim * 2
21795 }) as u32;
21796 use cudarc::driver::sys::CUfunction_attribute_enum as A;
21797 f.set_attribute(
21798 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
21799 sh as i32,
21800 )?;
21801 sh
21802 } else {
21803 0
21804 };
21805 for &(r0, t_g, sp_g) in &groups {
21809 let n_splits_g = (base_len + r0 + t_g).div_ceil(sp_g);
21810 let (nspm, spk) = (n_splits_g as i32, sp_g as i32);
21811 let base_i = (base_len + r0) as i32;
21812 let o_len = t_g * n_head * n_splits_g * head_dim;
21813 let ml_len = t_g * n_head * n_splits_g;
21814 let mut part_guard = self.fa_part_pool.lock().unwrap();
21815 if part_guard
21816 .as_ref()
21817 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
21818 .unwrap_or(true)
21819 {
21820 let old = part_guard.take();
21831 let (co, cm) = old
21832 .as_ref()
21833 .map(|pp| (pp.0.len(), pp.1.len()))
21834 .unwrap_or((0, 0));
21835 if let Some(old) = old {
21836 self.fa_part_retired.lock().unwrap().push(old);
21837 }
21838 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
21839 eprintln!(
21840 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
21841 co, o_len, cm, ml_len
21842 );
21843 }
21844 *part_guard = Some((
21845 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
21846 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
21847 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
21848 ));
21849 }
21850 let pg = part_guard.as_mut().unwrap();
21851 self.gpu
21852 .stream()
21853 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
21854 self.gpu
21855 .stream()
21856 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
21857 self.gpu
21858 .stream()
21859 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
21860 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
21861 let (part_o, part_m, part_l) = (&mut *part_o, &mut *part_m, &mut *part_l);
21862 let qv = self.view(q, t * n_head * head_dim);
21863 let q_g = qv.slice(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
21864 let cfg = LaunchConfig {
21865 grid_dim: (n_head_kv as u32, n_splits_g as u32, t_g as u32),
21866 block_dim: (32, gqa, 1),
21867 shared_mem_bytes: shmem,
21868 };
21869 {
21870 let __s_b = self.gpu.stream();
21871 let mut b = __s_b.launch_builder(&f);
21872 if tb512 {
21873 let (bd, plus) =
21875 base_dev.expect("hd512 rows twin requires a device base counter");
21876 let plus_g = plus + r0 as i32;
21877 let nr = t_g as i32;
21878 if Self::pdl_on() && Self::pdl_wb_on() {
21879 use cudarc::driver::{DevicePtr, DevicePtrMut};
21881 let s = &self.gpu.stream();
21882 let (pq, _b0) = q_g.device_ptr(s);
21883 let (pk, _b1) = k.device_ptr(s);
21884 let (pv, _b2) = v.device_ptr(s);
21885 let (po, _b3) = part_o.device_ptr_mut(s);
21886 let (pm, _b4) = part_m.device_ptr_mut(s);
21887 let (pl, _b5) = part_l.device_ptr_mut(s);
21888 let (pb, _b6) = bd.device_ptr(s);
21889 let mut ps = [
21890 &pq as *const _ as *mut std::ffi::c_void,
21891 &pk as *const _ as *mut _,
21892 &pv as *const _ as *mut _,
21893 &po as *const _ as *mut _,
21894 &pm as *const _ as *mut _,
21895 &pl as *const _ as *mut _,
21896 &hd as *const _ as *mut _,
21897 &nh as *const _ as *mut _,
21898 &nhkv as *const _ as *mut _,
21899 &pb as *const _ as *mut _,
21900 &plus_g as *const _ as *mut _,
21901 &scale as *const _ as *mut _,
21902 &nspm as *const _ as *mut _,
21903 &spk as *const _ as *mut _,
21904 &ktb as *const _ as *mut _,
21905 &vtb as *const _ as *mut _,
21906 &nr as *const _ as *mut _,
21907 ];
21908 unsafe {
21909 self.launch_pdl_flash(
21910 Self::gkv_on(),
21911 "fa_decode_vec_q_rows_v4_512_tb",
21912 (n_head_kv as u32, n_splits_g as u32, 1),
21913 (32, gqa, 1),
21914 shmem,
21915 &mut ps,
21916 )?;
21917 }
21918 } else {
21919 let cfg_tb = LaunchConfig {
21920 grid_dim: (n_head_kv as u32, n_splits_g as u32, 1),
21921 block_dim: (32, gqa, 1),
21922 shared_mem_bytes: shmem,
21923 };
21924 b.arg(&q_g)
21925 .arg(k)
21926 .arg(v)
21927 .arg(&mut *part_o)
21928 .arg(&mut *part_m)
21929 .arg(&mut *part_l)
21930 .arg(&hd)
21931 .arg(&nh)
21932 .arg(&nhkv)
21933 .arg(bd)
21934 .arg(&plus_g)
21935 .arg(&scale)
21936 .arg(&nspm)
21937 .arg(&spk)
21938 .arg(&ktb)
21939 .arg(&vtb)
21940 .arg(&nr);
21941 unsafe {
21942 b.launch(cfg_tb)?;
21943 }
21944 }
21945 } else if head_dim == 512 {
21946 let (bd, plus) =
21947 base_dev.expect("hd512 rows twin requires a device base counter");
21948 let plus_g = plus + r0 as i32;
21949 b.arg(&q_g)
21950 .arg(k)
21951 .arg(v)
21952 .arg(&mut *part_o)
21953 .arg(&mut *part_m)
21954 .arg(&mut *part_l)
21955 .arg(&hd)
21956 .arg(&nh)
21957 .arg(&nhkv)
21958 .arg(bd)
21959 .arg(&plus_g)
21960 .arg(&scale)
21961 .arg(&nspm)
21962 .arg(&spk)
21963 .arg(&ktb)
21964 .arg(&vtb);
21965 unsafe {
21966 b.launch(cfg)?;
21967 }
21968 } else {
21969 b.arg(&q_g)
21970 .arg(k)
21971 .arg(v)
21972 .arg(&mut *part_o)
21973 .arg(&mut *part_m)
21974 .arg(&mut *part_l)
21975 .arg(&hd)
21976 .arg(&nh)
21977 .arg(&nhkv)
21978 .arg(&base_i)
21979 .arg(&scale)
21980 .arg(&nspm)
21981 .arg(&spk)
21982 .arg(&ktb)
21983 .arg(&vtb);
21984 unsafe {
21985 b.launch(cfg)?;
21986 }
21987 }
21988 }
21989 let cfg2 = LaunchConfig {
21990 grid_dim: (n_head as u32, t_g as u32, 1),
21991 block_dim: (head_dim as u32, 1, 1),
21992 shared_mem_bytes: 0,
21993 };
21994 let mut o_g = o.slice_mut(r0 * n_head * head_dim..(r0 + t_g) * n_head * head_dim);
21995 if head_dim == 512 {
21996 let (bd, plus) = base_dev.unwrap();
21999 let plus_g = plus + r0 as i32;
22000 if let Some((oq, od)) = q8_out.as_mut() {
22001 debug_assert!(t == 1, "rows q8 emit is a t=1 decode arm");
22003 if Self::pdl_on() && Self::pdl_wb_on() {
22004 use cudarc::driver::{DevicePtr, DevicePtrMut};
22006 let s = &self.gpu.stream();
22007 let (po, _g0) = part_o.device_ptr(s);
22008 let (pm, _g1) = part_m.device_ptr(s);
22009 let (pl, _g2) = part_l.device_ptr(s);
22010 let (pq, _g3) = oq.device_ptr_mut(s);
22011 let (pd, _g4) = od.device_ptr_mut(s);
22012 let (pb, _g5) = bd.device_ptr(s);
22013 let mut ps = [
22014 &po as *const _ as *mut std::ffi::c_void,
22015 &pm as *const _ as *mut _,
22016 &pl as *const _ as *mut _,
22017 &pq as *const _ as *mut _,
22018 &pd as *const _ as *mut _,
22019 &hd as *const _ as *mut _,
22020 &nh as *const _ as *mut _,
22021 &pb as *const _ as *mut _,
22022 &plus_g as *const _ as *mut _,
22023 &nspm as *const _ as *mut _,
22024 &spk as *const _ as *mut _,
22025 ];
22026 unsafe {
22027 self.launch_pdl_flash(
22028 Self::gkv_on(),
22029 "fa_decode_combine_rows_dc_q8_1",
22030 cfg2.grid_dim,
22031 cfg2.block_dim,
22032 0,
22033 &mut ps,
22034 )?;
22035 }
22036 continue;
22037 }
22038 let fc = self.fa_func("fa_decode_combine_rows_dc_q8_1", head_dim);
22039 let __s_b2 = self.gpu.stream();
22040 let mut b2 = __s_b2.launch_builder(&fc);
22041 b2.arg(&*part_o)
22042 .arg(&*part_m)
22043 .arg(&*part_l)
22044 .arg(&mut **oq)
22045 .arg(&mut **od)
22046 .arg(&hd)
22047 .arg(&nh)
22048 .arg(bd)
22049 .arg(&plus_g)
22050 .arg(&nspm)
22051 .arg(&spk);
22052 unsafe {
22053 b2.launch(cfg2)?;
22054 }
22055 continue;
22056 }
22057 let fc = self.fa_func("fa_decode_combine_rows_dc", head_dim);
22058 let __s_b2 = self.gpu.stream();
22059 let mut b2 = __s_b2.launch_builder(&fc);
22060 b2.arg(&*part_o)
22061 .arg(&*part_m)
22062 .arg(&*part_l)
22063 .arg(&mut o_g)
22064 .arg(&hd)
22065 .arg(&nh)
22066 .arg(bd)
22067 .arg(&plus_g)
22068 .arg(&nspm)
22069 .arg(&spk);
22070 unsafe {
22071 b2.launch(cfg2)?;
22072 }
22073 } else {
22074 assert!(
22077 q8_out.is_none(),
22078 "rows q8 emit requires the hd512 dc combine"
22079 );
22080 let fc = self.func("fa_decode_combine_rows");
22081 let __s_b2 = self.gpu.stream();
22082 let mut b2 = __s_b2.launch_builder(&fc);
22083 b2.arg(&*part_o)
22084 .arg(&*part_m)
22085 .arg(&*part_l)
22086 .arg(&mut o_g)
22087 .arg(&hd)
22088 .arg(&nh)
22089 .arg(&base_i)
22090 .arg(&nspm)
22091 .arg(&spk);
22092 unsafe {
22093 b2.launch(cfg2)?;
22094 }
22095 }
22096 }
22097 Ok(())
22098 }
22099
22100 #[allow(clippy::too_many_arguments)]
22104 pub fn fa_decode_rows_w(
22105 &self,
22106 q: &CudaSlice<f32>,
22107 k: &cudarc::driver::CudaView<u8>,
22108 v: &cudarc::driver::CudaView<u8>,
22109 o: &mut CudaSlice<f32>,
22110 head_dim: usize,
22111 n_head: usize,
22112 n_head_kv: usize,
22113 base_dev: &CudaSlice<i32>,
22114 base_plus: i32,
22115 t: usize,
22116 scale: f32,
22117 window: usize,
22118 k_tok_bytes: usize,
22119 v_tok_bytes: usize,
22120 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
22121 ) -> Result<(), Box<dyn std::error::Error>> {
22122 debug_assert!(head_dim == 256);
22127 let sp = {
22135 static SPW: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
22136 let v = *SPW.get_or_init(|| {
22137 std::env::var("MEMRA_FA_SPW")
22138 .ok()
22139 .and_then(|x| x.parse().ok())
22140 .unwrap_or(0)
22141 });
22142 if v >= 8 {
22143 v
22144 } else {
22145 FA_SPW_DEFAULT.load(std::sync::atomic::Ordering::Relaxed)
22146 }
22147 };
22148 let n_splits_max = (window + sp - 1) / sp;
22149 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
22150 let (nspm, spk, wini) = (n_splits_max as i32, sp as i32, window as i32);
22151 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
22152 let gqa = (n_head / n_head_kv).max(1) as u32;
22153 let o_len = t * n_head * n_splits_max * head_dim;
22154 let ml_len = t * n_head * n_splits_max;
22155 let mut part_guard = self.fa_part_pool.lock().unwrap();
22156 if part_guard
22157 .as_ref()
22158 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
22159 .unwrap_or(true)
22160 {
22161 let old = part_guard.take();
22172 let (co, cm) = old
22173 .as_ref()
22174 .map(|pp| (pp.0.len(), pp.1.len()))
22175 .unwrap_or((0, 0));
22176 if let Some(old) = old {
22177 self.fa_part_retired.lock().unwrap().push(old);
22178 }
22179 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
22180 eprintln!(
22181 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
22182 co, o_len, cm, ml_len
22183 );
22184 }
22185 *part_guard = Some((
22186 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
22187 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
22188 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
22189 ));
22190 }
22191 let pg = part_guard.as_mut().unwrap();
22192 self.gpu
22193 .stream()
22194 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
22195 self.gpu
22196 .stream()
22197 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
22198 self.gpu
22199 .stream()
22200 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
22201 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
22202 static SMEM_TKV_W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
22208 let smem_tkv = *SMEM_TKV_W.get_or_init(|| {
22209 std::env::var("MEMRA_FA_SMEM_TKV")
22210 .ok()
22211 .and_then(|v| v.parse().ok())
22212 .unwrap_or_else(|| FA_SMEM_TKV_DEFAULT.load(std::sync::atomic::Ordering::Relaxed))
22213 });
22214 use cudarc::driver::sys::CUfunction_attribute_enum as A;
22220 let wg = Self::wkv_on();
22225 let sp2 =
22228 gqa <= 4 && fa_v4_at(window) && std::env::var("MEMRA_FA_SPW2").as_deref() != Ok("0");
22229 if sp2 {
22230 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
22231 if Self::pdl_on() && Self::pdl_wb_on() {
22232 use cudarc::driver::{DevicePtr, DevicePtrMut};
22234 let s = &self.gpu.stream();
22235 let (pq, _b0) = q.device_ptr(s);
22236 let (pk, _b1) = k.device_ptr(s);
22237 let (pv, _b2) = v.device_ptr(s);
22238 let (po, _b3) = part_o.device_ptr_mut(s);
22239 let (pm, _b4) = part_m.device_ptr_mut(s);
22240 let (pl, _b5) = part_l.device_ptr_mut(s);
22241 let (pb, _b6) = base_dev.device_ptr(s);
22242 let mut ps = [
22243 &pq as *const _ as *mut std::ffi::c_void,
22244 &pk as *const _ as *mut _,
22245 &pv as *const _ as *mut _,
22246 &po as *const _ as *mut _,
22247 &pm as *const _ as *mut _,
22248 &pl as *const _ as *mut _,
22249 &hd as *const _ as *mut _,
22250 &nh as *const _ as *mut _,
22251 &nhkv as *const _ as *mut _,
22252 &pb as *const _ as *mut _,
22253 &base_plus as *const _ as *mut _,
22254 &scale as *const _ as *mut _,
22255 &nspm as *const _ as *mut _,
22256 &spk as *const _ as *mut _,
22257 &ktb as *const _ as *mut _,
22258 &vtb as *const _ as *mut _,
22259 &wini as *const _ as *mut _,
22260 ];
22261 unsafe {
22262 self.launch_pdl_flash(
22263 wg,
22264 "fa_decode_vec_q_rows_v4_w_sp",
22265 (n_head_kv as u32, n_splits_max as u32, t as u32),
22266 (32, gqa + 1, 1),
22267 sh,
22268 &mut ps,
22269 )?;
22270 }
22271 } else {
22272 let f = if wg {
22273 self.func_g("fa_decode_vec_q_rows_v4_w_sp")
22274 } else {
22275 self.func("fa_decode_vec_q_rows_v4_w_sp")
22276 };
22277 f.set_attribute(
22278 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22279 sh as i32,
22280 )?;
22281 let cfg = LaunchConfig {
22282 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
22283 block_dim: (32, gqa + 1, 1),
22284 shared_mem_bytes: sh,
22285 };
22286 let __s_b = self.gpu.stream();
22287 let mut b = __s_b.launch_builder(&f);
22288 b.arg(q)
22289 .arg(k)
22290 .arg(v)
22291 .arg(&mut *part_o)
22292 .arg(&mut *part_m)
22293 .arg(&mut *part_l)
22294 .arg(&hd)
22295 .arg(&nh)
22296 .arg(&nhkv)
22297 .arg(base_dev)
22298 .arg(&base_plus)
22299 .arg(&scale)
22300 .arg(&nspm)
22301 .arg(&spk)
22302 .arg(&ktb)
22303 .arg(&vtb)
22304 .arg(&wini);
22305 unsafe {
22306 b.launch(cfg)?;
22307 }
22308 }
22309 } else {
22310 if fa_v4_at(window) && Self::pdl_on() && Self::pdl_wb_on() {
22311 let sh = (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32;
22313 use cudarc::driver::{DevicePtr, DevicePtrMut};
22314 let s = &self.gpu.stream();
22315 let (pq, _b0) = q.device_ptr(s);
22316 let (pk, _b1) = k.device_ptr(s);
22317 let (pv, _b2) = v.device_ptr(s);
22318 let (po, _b3) = part_o.device_ptr_mut(s);
22319 let (pm, _b4) = part_m.device_ptr_mut(s);
22320 let (pl, _b5) = part_l.device_ptr_mut(s);
22321 let (pb, _b6) = base_dev.device_ptr(s);
22322 let mut ps = [
22323 &pq as *const _ as *mut std::ffi::c_void,
22324 &pk as *const _ as *mut _,
22325 &pv as *const _ as *mut _,
22326 &po as *const _ as *mut _,
22327 &pm as *const _ as *mut _,
22328 &pl as *const _ as *mut _,
22329 &hd as *const _ as *mut _,
22330 &nh as *const _ as *mut _,
22331 &nhkv as *const _ as *mut _,
22332 &pb as *const _ as *mut _,
22333 &base_plus as *const _ as *mut _,
22334 &scale as *const _ as *mut _,
22335 &nspm as *const _ as *mut _,
22336 &spk as *const _ as *mut _,
22337 &ktb as *const _ as *mut _,
22338 &vtb as *const _ as *mut _,
22339 &wini as *const _ as *mut _,
22340 ];
22341 unsafe {
22342 self.launch_pdl_flash(
22343 wg,
22344 "fa_decode_vec_q_rows_v4_w",
22345 (n_head_kv as u32, n_splits_max as u32, t as u32),
22346 (32, gqa, 1),
22347 sh,
22348 &mut ps,
22349 )?;
22350 }
22351 } else {
22352 let pick = |name: &str| {
22353 if wg {
22354 self.func_g(name)
22355 } else {
22356 self.func(name)
22357 }
22358 };
22359 let (f, sh) = if fa_v4_at(window) {
22360 let f = pick("fa_decode_vec_q_rows_v4_w");
22361 (f, (11520 + 32 * head_dim * if wg { 1 } else { 2 }) as u32)
22362 } else if smem_tkv > 0 && window >= smem_tkv {
22363 (
22366 pick("fa_decode_vec_q_rows_smem_w"),
22367 (2 * 32 * head_dim * 2) as u32,
22368 )
22369 } else {
22370 (pick("fa_decode_vec_q_rows_reg_w"), 0u32)
22371 };
22372 f.set_attribute(
22373 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22374 sh as i32,
22375 )?;
22376 let cfg = LaunchConfig {
22377 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
22378 block_dim: (32, gqa, 1),
22379 shared_mem_bytes: sh,
22380 };
22381 let __s_b = self.gpu.stream();
22382 let mut b = __s_b.launch_builder(&f);
22383 b.arg(q)
22384 .arg(k)
22385 .arg(v)
22386 .arg(&mut *part_o)
22387 .arg(&mut *part_m)
22388 .arg(&mut *part_l)
22389 .arg(&hd)
22390 .arg(&nh)
22391 .arg(&nhkv)
22392 .arg(base_dev)
22393 .arg(&base_plus)
22394 .arg(&scale)
22395 .arg(&nspm)
22396 .arg(&spk)
22397 .arg(&ktb)
22398 .arg(&vtb)
22399 .arg(&wini);
22400 unsafe {
22401 b.launch(cfg)?;
22402 }
22403 }
22404 }
22405 let cfg2 = LaunchConfig {
22406 grid_dim: (n_head as u32, t as u32, 1),
22407 block_dim: (head_dim as u32, 1, 1),
22408 shared_mem_bytes: 0,
22409 };
22410 if let Some((oq, od)) = q8_out {
22411 if Self::pdl_on() && Self::pdl_wb_on() {
22414 use cudarc::driver::{DevicePtr, DevicePtrMut};
22416 let s = &self.gpu.stream();
22417 let (po, _g0) = part_o.device_ptr(s);
22418 let (pm, _g1) = part_m.device_ptr(s);
22419 let (pl, _g2) = part_l.device_ptr(s);
22420 let (pq, _g3) = oq.device_ptr_mut(s);
22421 let (pd, _g4) = od.device_ptr_mut(s);
22422 let mut ps = [
22423 &po as *const _ as *mut std::ffi::c_void,
22424 &pm as *const _ as *mut _,
22425 &pl as *const _ as *mut _,
22426 &pq as *const _ as *mut _,
22427 &pd as *const _ as *mut _,
22428 &hd as *const _ as *mut _,
22429 &nh as *const _ as *mut _,
22430 &nspm as *const _ as *mut _,
22431 &spk as *const _ as *mut _,
22432 &wini as *const _ as *mut _,
22433 ];
22434 unsafe {
22435 self.launch_pdl_flash(
22436 wg,
22437 "fa_decode_combine_rows_w_q8_1",
22438 cfg2.grid_dim,
22439 cfg2.block_dim,
22440 0,
22441 &mut ps,
22442 )?;
22443 }
22444 return Ok(());
22445 }
22446 let fc = if wg {
22447 self.func_g("fa_decode_combine_rows_w_q8_1")
22448 } else {
22449 self.func("fa_decode_combine_rows_w_q8_1")
22450 };
22451 let __s_b2 = self.gpu.stream();
22452 let mut b2 = __s_b2.launch_builder(&fc);
22453 b2.arg(&*part_o)
22454 .arg(&*part_m)
22455 .arg(&*part_l)
22456 .arg(oq)
22457 .arg(od)
22458 .arg(&hd)
22459 .arg(&nh)
22460 .arg(&nspm)
22461 .arg(&spk)
22462 .arg(&wini);
22463 unsafe {
22464 b2.launch(cfg2)?;
22465 }
22466 return Ok(());
22467 }
22468 let fc = if wg {
22469 self.func_g("fa_decode_combine_rows_w")
22470 } else {
22471 self.func("fa_decode_combine_rows_w")
22472 };
22473 let __s_b2 = self.gpu.stream();
22474 let mut b2 = __s_b2.launch_builder(&fc);
22475 b2.arg(&*part_o)
22476 .arg(&*part_m)
22477 .arg(&*part_l)
22478 .arg(o)
22479 .arg(&hd)
22480 .arg(&nh)
22481 .arg(&nspm)
22482 .arg(&spk)
22483 .arg(&wini);
22484 unsafe {
22485 b2.launch(cfg2)?;
22486 }
22487 Ok(())
22488 }
22489
22490 #[allow(clippy::too_many_arguments)]
22496 pub fn fa_decode_rows_dc(
22497 &self,
22498 q: &CudaSlice<f32>,
22499 k: &cudarc::driver::CudaView<u8>,
22500 v: &cudarc::driver::CudaView<u8>,
22501 o: &mut CudaSlice<f32>,
22502 head_dim: usize,
22503 n_head: usize,
22504 n_head_kv: usize,
22505 base_dev: &CudaSlice<i32>,
22506 t_kv_upper: usize,
22507 t: usize,
22508 scale: f32,
22509 k_tok_bytes: usize,
22510 v_tok_bytes: usize,
22511 base_plus: i32,
22512 g: bool,
22513 ) -> Result<(), Box<dyn std::error::Error>> {
22514 let v4 = head_dim == 256 && fa_v4_at(t_kv_upper);
22515 assert!(
22516 v4 || fa_v3_active(head_dim),
22517 "stream fa rows requires the v3 or v4 lane"
22518 );
22519 assert!(v4 || base_plus == 0, "v3_dc kernel takes no plus arg");
22520 if v4 {
22521 let sp = fa_split_keys(t_kv_upper, n_head_kv);
22522 let n_splits_max = (t_kv_upper + sp - 1) / sp;
22523 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
22524 let (nspm, spk) = (n_splits_max as i32, sp as i32);
22525 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
22526 let gqa = (n_head / n_head_kv).max(1) as u32;
22527 let o_len = t * n_head * n_splits_max * head_dim;
22528 let ml_len = t * n_head * n_splits_max;
22529 let mut part_guard = self.fa_part_pool.lock().unwrap();
22530 if part_guard
22531 .as_ref()
22532 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
22533 .unwrap_or(true)
22534 {
22535 let old = part_guard.take();
22546 let (co, cm) = old
22547 .as_ref()
22548 .map(|pp| (pp.0.len(), pp.1.len()))
22549 .unwrap_or((0, 0));
22550 if let Some(old) = old {
22551 self.fa_part_retired.lock().unwrap().push(old);
22552 }
22553 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
22554 eprintln!(
22555 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
22556 co, o_len, cm, ml_len
22557 );
22558 }
22559 *part_guard = Some((
22560 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
22561 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
22562 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
22563 ));
22564 }
22565 let pg = part_guard.as_mut().unwrap();
22566 self.gpu
22567 .stream()
22568 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
22569 self.gpu
22570 .stream()
22571 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
22572 self.gpu
22573 .stream()
22574 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
22575 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
22576 let f = if g {
22577 self.func_g("fa_decode_vec_q_rows_v4_dc")
22578 } else {
22579 self.func("fa_decode_vec_q_rows_v4_dc")
22580 };
22581 let sh = (11520 + 32 * head_dim * if g { 1 } else { 2 }) as u32;
22582 use cudarc::driver::sys::CUfunction_attribute_enum as A;
22583 f.set_attribute(
22584 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22585 sh as i32,
22586 )?;
22587 let cfg = LaunchConfig {
22588 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
22589 block_dim: (32, gqa, 1),
22590 shared_mem_bytes: sh,
22591 };
22592 let __s_b = self.gpu.stream();
22593 let mut b = __s_b.launch_builder(&f);
22594 b.arg(q)
22595 .arg(k)
22596 .arg(v)
22597 .arg(&mut *part_o)
22598 .arg(&mut *part_m)
22599 .arg(&mut *part_l)
22600 .arg(&hd)
22601 .arg(&nh)
22602 .arg(&nhkv)
22603 .arg(base_dev)
22604 .arg(&base_plus)
22605 .arg(&scale)
22606 .arg(&nspm)
22607 .arg(&spk)
22608 .arg(&ktb)
22609 .arg(&vtb);
22610 unsafe {
22611 b.launch(cfg)?;
22612 }
22613 let fc = self.func("fa_decode_combine_rows_dc");
22614 let cfg2 = LaunchConfig {
22615 grid_dim: (n_head as u32, t as u32, 1),
22616 block_dim: (head_dim as u32, 1, 1),
22617 shared_mem_bytes: 0,
22618 };
22619 let __s_b2 = self.gpu.stream();
22620 let mut b2 = __s_b2.launch_builder(&fc);
22621 b2.arg(&*part_o)
22622 .arg(&*part_m)
22623 .arg(&*part_l)
22624 .arg(o)
22625 .arg(&hd)
22626 .arg(&nh)
22627 .arg(base_dev)
22628 .arg(&base_plus)
22629 .arg(&nspm)
22630 .arg(&spk);
22631 unsafe {
22632 b2.launch(cfg2)?;
22633 }
22634 return Ok(());
22635 }
22636 let sp = fa_split_keys(t_kv_upper, n_head_kv);
22637 let n_splits_max = (t_kv_upper + sp - 1) / sp;
22638 let (hd, nh, nhkv) = (head_dim as i32, n_head as i32, n_head_kv as i32);
22639 let (nspm, spk) = (n_splits_max as i32, sp as i32);
22640 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
22641 let gqa = (n_head / n_head_kv).max(1) as u32;
22642 let o_len = t * n_head * n_splits_max * head_dim;
22643 let ml_len = t * n_head * n_splits_max;
22644 let mut part_guard = self.fa_part_pool.lock().unwrap();
22645 if part_guard
22646 .as_ref()
22647 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
22648 .unwrap_or(true)
22649 {
22650 let old = part_guard.take();
22661 let (co, cm) = old
22662 .as_ref()
22663 .map(|pp| (pp.0.len(), pp.1.len()))
22664 .unwrap_or((0, 0));
22665 if let Some(old) = old {
22666 self.fa_part_retired.lock().unwrap().push(old);
22667 }
22668 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
22669 eprintln!(
22670 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
22671 co, o_len, cm, ml_len
22672 );
22673 }
22674 *part_guard = Some((
22675 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
22676 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
22677 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
22678 ));
22679 }
22680 let pg = part_guard.as_mut().unwrap();
22681 self.gpu
22682 .stream()
22683 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
22684 self.gpu
22685 .stream()
22686 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
22687 self.gpu
22688 .stream()
22689 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
22690 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
22691 let f = self.func("fa_decode_vec_q_rows_v3_dc");
22692 let sh = (32 * head_dim * 2) as u32;
22693 use cudarc::driver::sys::CUfunction_attribute_enum as A;
22694 f.set_attribute(
22695 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22696 sh as i32,
22697 )?;
22698 let cfg = LaunchConfig {
22699 grid_dim: (n_head_kv as u32, n_splits_max as u32, t as u32),
22700 block_dim: (32, gqa, 1),
22701 shared_mem_bytes: sh,
22702 };
22703 let __s_b = self.gpu.stream();
22704 let mut b = __s_b.launch_builder(&f);
22705 b.arg(q)
22706 .arg(k)
22707 .arg(v)
22708 .arg(&mut *part_o)
22709 .arg(&mut *part_m)
22710 .arg(&mut *part_l)
22711 .arg(&hd)
22712 .arg(&nh)
22713 .arg(&nhkv)
22714 .arg(base_dev)
22715 .arg(&scale)
22716 .arg(&nspm)
22717 .arg(&spk)
22718 .arg(&ktb)
22719 .arg(&vtb);
22720 unsafe {
22721 b.launch(cfg)?;
22722 }
22723 let fc = self.func("fa_decode_combine_rows_dc");
22724 let cfg2 = LaunchConfig {
22725 grid_dim: (n_head as u32, t as u32, 1),
22726 block_dim: (head_dim as u32, 1, 1),
22727 shared_mem_bytes: 0,
22728 };
22729 let plus0 = 0i32;
22730 let __s_b2 = self.gpu.stream();
22731 let mut b2 = __s_b2.launch_builder(&fc);
22732 b2.arg(&*part_o)
22733 .arg(&*part_m)
22734 .arg(&*part_l)
22735 .arg(o)
22736 .arg(&hd)
22737 .arg(&nh)
22738 .arg(base_dev)
22739 .arg(&plus0)
22740 .arg(&nspm)
22741 .arg(&spk);
22742 unsafe {
22743 b2.launch(cfg2)?;
22744 }
22745 Ok(())
22746 }
22747
22748 pub fn fa_decode_dc(
22759 &self,
22760 q: &CudaSlice<f32>,
22761 k: &cudarc::driver::CudaView<u8>,
22762 v: &cudarc::driver::CudaView<u8>,
22763 o: &mut CudaSlice<f32>,
22764 head_dim: usize,
22765 n_head: usize,
22766 n_head_kv: usize,
22767 t_kv_dev: &CudaSlice<i32>,
22768 bucket_max: usize,
22769 scale: f32,
22770 k_tok_bytes: usize,
22771 v_tok_bytes: usize,
22772 g: bool,
22773 ) -> Result<(), Box<dyn std::error::Error>> {
22774 self.fa_decode_dc_q8(
22775 q,
22776 k,
22777 v,
22778 o,
22779 head_dim,
22780 n_head,
22781 n_head_kv,
22782 t_kv_dev,
22783 bucket_max,
22784 scale,
22785 k_tok_bytes,
22786 v_tok_bytes,
22787 g,
22788 None,
22789 )
22790 }
22791
22792 #[allow(clippy::too_many_arguments)]
22795 pub fn fa_decode_dc_q8(
22796 &self,
22797 q: &CudaSlice<f32>,
22798 k: &cudarc::driver::CudaView<u8>,
22799 v: &cudarc::driver::CudaView<u8>,
22800 o: &mut CudaSlice<f32>,
22801 head_dim: usize,
22802 n_head: usize,
22803 n_head_kv: usize,
22804 t_kv_dev: &CudaSlice<i32>,
22805 bucket_max: usize,
22806 scale: f32,
22807 k_tok_bytes: usize,
22808 v_tok_bytes: usize,
22809 g: bool,
22810 q8_out: Option<(&mut CudaSlice<i8>, &mut CudaSlice<f32>)>,
22811 ) -> Result<(), Box<dyn std::error::Error>> {
22812 let mut fa_vec =
22820 std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
22821 if g && head_dim == 256 && !fa_v4_at(bucket_max) {
22822 fa_vec = false;
22823 } let sp = fa_split_keys(bucket_max, n_head_kv);
22825 let n_splits = if fa_vec {
22826 ((bucket_max + sp - 1) / sp).max(1)
22827 } else {
22828 ((bucket_max + 255) / 256).max(1)
22829 };
22830 let o_len = n_head * n_splits * head_dim;
22831 let ml_len = n_head * n_splits;
22832 let mut part_guard = self.fa_part_pool.lock().unwrap();
22833 if part_guard
22834 .as_ref()
22835 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
22836 .unwrap_or(true)
22837 {
22838 let old = part_guard.take();
22849 let (co, cm) = old
22850 .as_ref()
22851 .map(|pp| (pp.0.len(), pp.1.len()))
22852 .unwrap_or((0, 0));
22853 if let Some(old) = old {
22854 self.fa_part_retired.lock().unwrap().push(old);
22855 }
22856 if std::env::var("MEMRA_DEBUG_FAPOOL").is_ok() {
22857 eprintln!(
22858 "[fa-pool] REALLOC o {} -> {} ml {} -> {} (old retired)",
22859 co, o_len, cm, ml_len
22860 );
22861 }
22862 *part_guard = Some((
22863 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
22864 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
22865 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
22866 ));
22867 }
22868 let pg = part_guard.as_mut().unwrap();
22869 self.gpu
22870 .stream()
22871 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
22872 self.gpu
22873 .stream()
22874 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
22875 self.gpu
22876 .stream()
22877 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
22878 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
22879 let (hd, nh, nhkv, nsp) = (
22880 head_dim as i32,
22881 n_head as i32,
22882 n_head_kv as i32,
22883 n_splits as i32,
22884 );
22885 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
22886 let fa_vec = fa_vec && head_dim <= 512 && head_dim % 32 == 0;
22887 let deep = fa_vec
22890 && head_dim == 256
22891 && fa_v4_at(bucket_max)
22892 && !g
22893 && fa_deep_at(bucket_max)
22894 && !matches!(fa_v4_mode(), "noB3" | "stage");
22895 let (f, cfg) = if fa_vec
22896 && head_dim == 512
22897 && bucket_max >= {
22898 static FA512_MIN_DC: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
22899 *FA512_MIN_DC.get_or_init(|| {
22900 std::env::var("MEMRA_FA512_MIN")
22901 .ok()
22902 .and_then(|v| v.parse().ok())
22903 .unwrap_or(512)
22904 })
22905 } {
22906 let gqa = (n_head / n_head_kv).max(1) as u32;
22908 (
22909 self.fa_func("fa_decode_vec_q_dpl16_dc", head_dim),
22910 LaunchConfig {
22911 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
22912 block_dim: (32, gqa, 1),
22913 shared_mem_bytes: 0,
22914 },
22915 )
22916 } else if fa_vec && head_dim == 512 {
22917 let q_view = q.as_view();
22920 let mut o_view = o.as_view_mut();
22921 return self.fa_decode_scalar_unified(
22922 &q_view,
22923 k,
22924 v,
22925 &mut o_view,
22926 head_dim,
22927 n_head,
22928 n_head_kv,
22929 0,
22930 Some(t_kv_dev),
22931 scale,
22932 n_splits,
22933 sp,
22934 k_tok_bytes,
22935 v_tok_bytes,
22936 g,
22937 &mut *part_o,
22938 &mut *part_m,
22939 &mut *part_l,
22940 q8_out,
22941 );
22942 } else if fa_vec && head_dim == 256 && fa_v4_at(bucket_max) {
22943 let gqa = (n_head / n_head_kv).max(1) as u32;
22946 let fv = if g {
22947 self.func_g("fa_decode_vec_q_v4_dc")
22948 } else if deep {
22949 self.func("fa_decode_vec_q_v4_deep_dc")
22950 } else {
22951 self.func("fa_decode_vec_q_v4_dc")
22952 };
22953 let shmem =
22954 (if deep { 12160 } else { 11520 } + 32 * head_dim * if g { 1 } else { 2 }) as u32;
22955 use cudarc::driver::sys::CUfunction_attribute_enum as A;
22956 fv.set_attribute(
22957 A::CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
22958 shmem as i32,
22959 )?;
22960 (
22961 fv,
22962 LaunchConfig {
22963 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
22964 block_dim: (32, gqa, 1),
22965 shared_mem_bytes: shmem,
22966 },
22967 )
22968 } else if fa_vec && fa_v3_active(head_dim) {
22969 let gqa = (n_head / n_head_kv).max(1) as u32;
22972 let fv = if g {
22973 self.func_g("fa_decode_vec_q_v3_dc")
22974 } else {
22975 self.func("fa_decode_vec_q_v3_dc")
22976 };
22977 let shmem = (32 * head_dim * 2) as u32; (
22979 fv,
22980 LaunchConfig {
22981 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
22982 block_dim: (32, gqa, 1),
22983 shared_mem_bytes: shmem,
22984 },
22985 )
22986 } else if fa_vec && fa_v2_on() {
22987 let gqa = (n_head / n_head_kv).max(1) as u32;
22991 let fv = if g {
22992 self.func_g("fa_decode_vec_q_v2_dc")
22993 } else {
22994 self.func("fa_decode_vec_q_v2_dc")
22995 };
22996 let shmem = (2 * 32 * head_dim * 2) as u32; (
22998 fv,
22999 LaunchConfig {
23000 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
23001 block_dim: (32, gqa, 1),
23002 shared_mem_bytes: shmem,
23003 },
23004 )
23005 } else if fa_vec {
23006 let gqa = (n_head / n_head_kv).max(1) as u32;
23007 let fv = if g {
23009 self.func_g("fa_decode_vec_q_dc")
23010 } else {
23011 self.func("fa_decode_vec_q_dc")
23012 };
23013 (
23014 fv,
23015 LaunchConfig {
23016 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
23017 block_dim: (32, gqa, 1),
23018 shared_mem_bytes: 0,
23019 },
23020 )
23021 } else {
23022 let q_view = q.as_view();
23023 let mut o_view = o.as_view_mut();
23024 return self.fa_decode_scalar_unified(
23025 &q_view,
23026 k,
23027 v,
23028 &mut o_view,
23029 head_dim,
23030 n_head,
23031 n_head_kv,
23032 0,
23033 Some(t_kv_dev),
23034 scale,
23035 n_splits,
23036 if fa_vec { sp } else { 256 },
23037 k_tok_bytes,
23038 v_tok_bytes,
23039 g,
23040 &mut *part_o,
23041 &mut *part_m,
23042 &mut *part_l,
23043 q8_out,
23044 );
23045 };
23046 let ski = sp as i32; let __s_b = self.gpu.stream();
23048 let mut b = __s_b.launch_builder(&f);
23049 b.arg(q)
23050 .arg(k)
23051 .arg(v)
23052 .arg(&mut *part_o)
23053 .arg(&mut *part_m)
23054 .arg(&mut *part_l)
23055 .arg(&hd)
23056 .arg(&nh)
23057 .arg(&nhkv)
23058 .arg(t_kv_dev)
23059 .arg(&scale)
23060 .arg(&nsp)
23061 .arg(&ski)
23062 .arg(&ktb)
23063 .arg(&vtb);
23064 unsafe {
23065 b.launch(cfg)?;
23066 }
23067 let cfg2 = LaunchConfig {
23068 grid_dim: (n_head as u32, 1, 1),
23069 block_dim: (head_dim as u32, 1, 1),
23070 shared_mem_bytes: 0,
23071 };
23072 if let Some((oq, od)) = q8_out {
23073 let fc = if g {
23074 self.func_g("fa_decode_combine_q8_1")
23075 } else {
23076 self.fa_func("fa_decode_combine_q8_1", head_dim)
23077 };
23078 let __s_b2 = self.gpu.stream();
23079 let mut b2 = __s_b2.launch_builder(&fc);
23080 b2.arg(&*part_o)
23081 .arg(&*part_m)
23082 .arg(&*part_l)
23083 .arg(oq)
23084 .arg(od)
23085 .arg(&hd)
23086 .arg(&nh)
23087 .arg(&nsp);
23088 unsafe {
23089 b2.launch(cfg2)?;
23090 }
23091 return Ok(());
23092 }
23093 let fc = if g {
23094 self.func_g("fa_decode_combine_f32")
23095 } else {
23096 self.fa_func("fa_decode_combine_f32", head_dim)
23097 };
23098 let __s_b2 = self.gpu.stream();
23099 let mut b2 = __s_b2.launch_builder(&fc);
23100 b2.arg(&*part_o)
23101 .arg(&*part_m)
23102 .arg(&*part_l)
23103 .arg(o)
23104 .arg(&hd)
23105 .arg(&nh)
23106 .arg(&nsp);
23107 unsafe {
23108 b2.launch(cfg2)?;
23109 }
23110 Ok(())
23111 }
23112
23113 #[allow(clippy::too_many_arguments)]
23117 pub fn append_kv_quantized_dcw(
23118 &self,
23119 k_row: &CudaSlice<f32>,
23120 v_row: &CudaSlice<f32>,
23121 kc: &mut CudaSlice<u8>,
23122 vc: &mut CudaSlice<u8>,
23123 len_dev: &CudaSlice<i32>,
23124 base_dev: Option<&CudaSlice<i32>>,
23125 kv_dim_k: usize,
23126 kv_dim_v: usize,
23127 k_tok_bytes: usize,
23128 v_tok_bytes: usize,
23129 ) -> Result<(), Box<dyn std::error::Error>> {
23130 let f = self.func("append_quantize_kv_q8_0_q5_1_dcw");
23131 let nblk = (kv_dim_k.max(kv_dim_v) / 32) as u32;
23132 let cfg = LaunchConfig {
23133 grid_dim: (nblk, 1, 1),
23134 block_dim: (32, 1, 1),
23135 shared_mem_bytes: 0,
23136 };
23137 let (kdk, kdv) = (kv_dim_k as i32, kv_dim_v as i32);
23138 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
23139 let null: u64 = 0;
23140 let __s_b = self.gpu.stream();
23141 let mut b = __s_b.launch_builder(&f);
23142 b.arg(k_row).arg(v_row).arg(kc).arg(vc).arg(len_dev);
23143 match base_dev {
23144 Some(base) => {
23145 b.arg(base);
23146 }
23147 None => {
23148 b.arg(&null);
23149 }
23150 }
23151 b.arg(&kdk).arg(&kdv).arg(&ktb).arg(&vtb);
23152 unsafe {
23153 b.launch(cfg)?;
23154 }
23155 Ok(())
23156 }
23157
23158 pub fn inc_i32(&self, counter: &mut CudaSlice<i32>) -> Result<(), Box<dyn std::error::Error>> {
23160 let f = self.func("inc_i32");
23161 let cfg = LaunchConfig {
23162 grid_dim: (1, 1, 1),
23163 block_dim: (1, 1, 1),
23164 shared_mem_bytes: 0,
23165 };
23166 let __s_b = self.gpu.stream();
23167 let mut b = __s_b.launch_builder(&f);
23168 b.arg(counter);
23169 unsafe {
23170 b.launch(cfg)?;
23171 }
23172 Ok(())
23173 }
23174
23175 #[allow(clippy::too_many_arguments)]
23184 fn fa_part_pool_grow(
23188 &self,
23189 part_guard: &mut Option<(CudaSlice<f32>, CudaSlice<f32>, CudaSlice<f32>)>,
23190 o_len: usize,
23191 ml_len: usize,
23192 ) -> Result<(), Box<dyn std::error::Error>> {
23193 if part_guard
23194 .as_ref()
23195 .map(|pp| pp.0.len() < o_len || pp.1.len() < ml_len)
23196 .unwrap_or(true)
23197 {
23198 let old = part_guard.take();
23199 let (co, cm) = old
23200 .as_ref()
23201 .map(|pp| (pp.0.len(), pp.1.len()))
23202 .unwrap_or((0, 0));
23203 if let Some(old) = old {
23204 self.fa_part_retired.lock().unwrap().push(old);
23205 }
23206 *part_guard = Some((
23207 self.alloc_uninit::<f32>(o_len.max(2 * co))?,
23208 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
23209 self.alloc_uninit::<f32>(ml_len.max(2 * cm))?,
23210 ));
23211 }
23212 Ok(())
23213 }
23214
23215 pub fn fa_dcw_pool_ensure(
23218 &self,
23219 head_dim: usize,
23220 n_head: usize,
23221 n_head_kv: usize,
23222 bucket_max: usize,
23223 ) -> Result<(), Box<dyn std::error::Error>> {
23224 let sp = fa_split_keys(bucket_max, n_head_kv);
23225 let n_splits = ((bucket_max + sp - 1) / sp).max(1);
23226 let o_len = n_head * n_splits * head_dim;
23227 let ml_len = n_head * n_splits;
23228 let mut part_guard = self.fa_part_pool.lock().unwrap();
23229 self.fa_part_pool_grow(&mut part_guard, o_len, ml_len)
23230 }
23231
23232 #[allow(clippy::too_many_arguments)]
23240 pub fn fa_decode_dcw2(
23241 &self,
23242 q2: &CudaSlice<f32>,
23243 k_ring: &cudarc::driver::CudaView<u8>,
23244 v_ring: &cudarc::driver::CudaView<u8>,
23245 o2: &mut CudaSlice<f32>,
23246 head_dim: usize,
23247 n_head: usize,
23248 n_head_kv: usize,
23249 len_dev: &CudaSlice<i32>,
23250 base_dev: Option<&CudaSlice<i32>>,
23251 window: usize,
23252 bucket_max: usize,
23253 scale: f32,
23254 k_tok_bytes: usize,
23255 v_tok_bytes: usize,
23256 gate2: &CudaSlice<f32>,
23257 ) -> Result<(), Box<dyn std::error::Error>> {
23258 let fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
23259 if !fa_vec || head_dim > 256 || head_dim % 32 != 0 || !fa_v3_on() {
23260 return Err("fa_decode_dcw2 supports the default v3-vec class only".into());
23261 }
23262 let sp = fa_split_keys(bucket_max, n_head_kv);
23263 let n_splits = ((bucket_max + sp - 1) / sp).max(1);
23264 let o_len = 2 * n_head * n_splits * head_dim;
23266 let ml_len = 2 * n_head * n_splits;
23267 let mut part_guard = self.fa_part_pool.lock().unwrap();
23268 Self::fa_part_pool_grow(self, &mut part_guard, o_len, ml_len)?;
23269 let pg = part_guard.as_mut().unwrap();
23270 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
23271 let (hd, nh, nhkv, nsp) = (
23272 head_dim as i32,
23273 n_head as i32,
23274 n_head_kv as i32,
23275 n_splits as i32,
23276 );
23277 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
23278 let (ski, win) = (sp as i32, window as i32);
23279 let gqa = (n_head / n_head_kv).max(1) as u32;
23280 let smem = (32 * head_dim * 2) as u32;
23281 let f = self.func("fa_decode_vec_q_v3_dcw2");
23282 let cfg = LaunchConfig {
23283 grid_dim: (n_head_kv as u32, n_splits as u32, 1),
23284 block_dim: (32, gqa, 1),
23285 shared_mem_bytes: smem,
23286 };
23287 let null: u64 = 0;
23288 {
23289 let __s_b = self.gpu.stream();
23290 let mut b = __s_b.launch_builder(&f);
23291 b.arg(q2)
23292 .arg(k_ring)
23293 .arg(v_ring)
23294 .arg(&mut *part_o)
23295 .arg(&mut *part_m)
23296 .arg(&mut *part_l)
23297 .arg(&hd)
23298 .arg(&nh)
23299 .arg(&nhkv)
23300 .arg(len_dev);
23301 match base_dev {
23302 Some(base) => {
23303 b.arg(base);
23304 }
23305 None => {
23306 b.arg(&null);
23307 }
23308 }
23309 b.arg(&win)
23310 .arg(&scale)
23311 .arg(&nsp)
23312 .arg(&ski)
23313 .arg(&ktb)
23314 .arg(&vtb);
23315 unsafe {
23316 b.launch(cfg)?;
23317 }
23318 }
23319 let fc = {
23323 static CS: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
23324 if *CS.get_or_init(|| std::env::var("MEMRA_FA_COMBINE_S").as_deref() == Ok("1")) {
23325 self.func("fa_decode_combine_gate_f32_s")
23326 } else {
23327 self.func("fa_decode_combine_gate_f32")
23328 }
23329 };
23330 let combine_shared = std::env::var("MEMRA_FA_COMBINE_S").as_deref() == Ok("1");
23331 let nh2 = (2 * n_head) as i32;
23332 let cfg2 = LaunchConfig {
23333 grid_dim: ((2 * n_head) as u32, 1, 1),
23334 block_dim: (head_dim as u32, 1, 1),
23335 shared_mem_bytes: if combine_shared {
23336 (2 * n_splits * 4) as u32
23337 } else {
23338 0
23339 },
23340 };
23341 let __s_b2 = self.gpu.stream();
23342 let mut b2 = __s_b2.launch_builder(&fc);
23343 b2.arg(&*part_o)
23344 .arg(&*part_m)
23345 .arg(&*part_l)
23346 .arg(gate2)
23347 .arg(o2)
23348 .arg(&hd)
23349 .arg(&nh2)
23350 .arg(&nsp);
23351 unsafe {
23352 b2.launch(cfg2)?;
23353 }
23354 Ok(())
23355 }
23356
23357 pub fn fa_decode_dcw(
23358 &self,
23359 q: &CudaSlice<f32>,
23360 k_ring: &cudarc::driver::CudaView<u8>,
23361 v_ring: &cudarc::driver::CudaView<u8>,
23362 o: &mut CudaSlice<f32>,
23363 head_dim: usize,
23364 n_head: usize,
23365 n_head_kv: usize,
23366 len_dev: &CudaSlice<i32>,
23367 base_dev: Option<&CudaSlice<i32>>,
23368 window: usize,
23369 bucket_max: usize,
23370 scale: f32,
23371 k_tok_bytes: usize,
23372 v_tok_bytes: usize,
23373 fused_gate: Option<&CudaSlice<f32>>,
23377 ) -> Result<(), Box<dyn std::error::Error>> {
23378 let fa_vec = std::env::var("MEMRA_NO_FA_VEC").is_err() && bucket_max >= fa_vec_min_tkv();
23379 if !fa_vec || head_dim > 256 || head_dim % 32 != 0 || !fa_v3_on() {
23380 return Err("fa_decode_dcw supports the default v3-vec class only (bucket >= vec floor, head_dim <= 256, MEMRA_FA_V3 on); keep eager outside it"
23381 .into());
23382 }
23383 let sp = fa_split_keys(bucket_max, n_head_kv);
23384 let n_splits = ((bucket_max + sp - 1) / sp).max(1);
23385 let o_len = n_head * n_splits * head_dim;
23386 let ml_len = n_head * n_splits;
23387 let mut part_guard = self.fa_part_pool.lock().unwrap();
23388 Self::fa_part_pool_grow(self, &mut part_guard, o_len, ml_len)?;
23389 let pg = part_guard.as_mut().unwrap();
23390 static MEMSET_ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
23395 let memset_on = *MEMSET_ON
23400 .get_or_init(|| std::env::var("MEMRA_FA_DCW_MEMSET").as_deref() != Ok("0"))
23401 || crate::tp::token_graph_building();
23402 if memset_on {
23403 self.gpu
23404 .stream()
23405 .memset_zeros(&mut pg.0.slice_mut(0..o_len))?;
23406 self.gpu
23407 .stream()
23408 .memset_zeros(&mut pg.1.slice_mut(0..ml_len))?;
23409 self.gpu
23410 .stream()
23411 .memset_zeros(&mut pg.2.slice_mut(0..ml_len))?;
23412 }
23413 let (part_o, part_m, part_l) = (&mut pg.0, &mut pg.1, &mut pg.2);
23414 let (hd, nh, nhkv, nsp) = (
23415 head_dim as i32,
23416 n_head as i32,
23417 n_head_kv as i32,
23418 n_splits as i32,
23419 );
23420 let (ktb, vtb) = (k_tok_bytes as i64, v_tok_bytes as i64);
23421 let (ski, win) = (sp as i32, window as i32);
23422 let gqa = (n_head / n_head_kv).max(1) as u32;
23423 let smem = (32 * head_dim * 2) as u32; static U8: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
23427 static HOIST: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
23428 let hoist = *HOIST.get_or_init(|| match std::env::var("MEMRA_FA_HOIST").as_deref() {
23429 Ok("2") => 2,
23430 Ok("1") => 1,
23431 _ => 0,
23432 });
23433 static FPROF: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
23438 let fprof = *FPROF.get_or_init(|| std::env::var("MEMRA_FA_PROF").as_deref() == Ok("1"));
23439 static PROF_BUF: std::sync::Mutex<Option<(usize, CudaSlice<u64>)>> =
23440 std::sync::Mutex::new(None);
23441 static HS: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
23445 let hs2 = *HS.get_or_init(|| std::env::var("MEMRA_FA_HSPLIT").as_deref() == Ok("2"))
23446 && (n_head / n_head_kv) % 2 == 0
23447 && (n_head / n_head_kv) >= 2;
23448 let f = if fprof {
23449 self.func("fa_decode_vec_q_v3_dcw_prof")
23450 } else if hs2 {
23451 self.func("fa_decode_vec_q_v3_dcw_hs2")
23452 } else if hoist == 2 {
23453 self.func("fa_decode_vec_q_v3_dcw_hc")
23455 } else if hoist == 1 {
23456 self.func("fa_decode_vec_q_v3_dcw_h")
23458 } else if *U8.get_or_init(|| std::env::var("MEMRA_FA_UNROLL").as_deref() == Ok("8")) {
23459 self.func("fa_decode_vec_q_v3_dcw_u8")
23460 } else {
23461 self.func("fa_decode_vec_q_v3_dcw")
23462 };
23463 let cfg = LaunchConfig {
23464 grid_dim: if hs2 {
23465 ((2 * n_head_kv) as u32, n_splits as u32, 1)
23466 } else {
23467 (n_head_kv as u32, n_splits as u32, 1)
23468 },
23469 block_dim: if hs2 { (32, gqa / 2, 1) } else { (32, gqa, 1) },
23470 shared_mem_bytes: smem,
23471 };
23472 let null: u64 = 0;
23473 let __s_b = self.gpu.stream();
23474 let mut b = __s_b.launch_builder(&f);
23475 b.arg(q)
23476 .arg(k_ring)
23477 .arg(v_ring)
23478 .arg(&mut *part_o)
23479 .arg(&mut *part_m)
23480 .arg(&mut *part_l)
23481 .arg(&hd)
23482 .arg(&nh)
23483 .arg(&nhkv)
23484 .arg(len_dev);
23485 match base_dev {
23486 Some(base) => {
23487 b.arg(base);
23488 }
23489 None => {
23490 b.arg(&null);
23491 }
23492 }
23493 b.arg(&win)
23494 .arg(&scale)
23495 .arg(&nsp)
23496 .arg(&ski)
23497 .arg(&ktb)
23498 .arg(&vtb);
23499 if fprof {
23500 let mut guard = PROF_BUF.lock().map_err(|_| "fa prof buffer lock")?;
23501 if guard
23502 .as_ref()
23503 .is_none_or(|(d, _)| *d != self.ctx().ordinal())
23504 {
23505 *guard = Some((self.ctx().ordinal(), self.htod_u64(&vec![0u64; 8])?));
23506 }
23507 let (_, buf) = guard.as_mut().expect("armed above");
23508 b.arg(&*buf);
23509 unsafe {
23510 b.launch(cfg)?;
23511 }
23512 static CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
23513 let n = CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + 1;
23514 if n % 430 == 0 {
23515 self.stream().synchronize()?;
23516 let h = self.dtoh_u64(buf)?;
23517 let phases = ["setup", "stageV", "b1_klo", "b2_soft", "sync", "b3_vacc"];
23518 let tot: u64 = h[..6].iter().sum();
23519 let mut line = format!("[fa-prof] calls={n} keys={} cycles={tot}", h[6]);
23520 for (i, name) in phases.iter().enumerate() {
23521 let pct = if tot > 0 {
23522 h[i] as f64 / tot as f64 * 100.0
23523 } else {
23524 0.0
23525 };
23526 line.push_str(&format!(" {name}={pct:.1}%"));
23527 }
23528 if h[6] > 0 {
23529 line.push_str(&format!(" cyc/key={:.0}", tot as f64 / h[6] as f64));
23530 }
23531 eprintln!("{line}");
23532 }
23533 } else {
23534 unsafe {
23535 b.launch(cfg)?;
23536 }
23537 }
23538 let mut combine_shared = false;
23539 let fc = if fused_gate.is_some() {
23540 static CS: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
23543 if *CS.get_or_init(|| std::env::var("MEMRA_FA_COMBINE_S").as_deref() == Ok("1")) {
23544 combine_shared = true;
23545 self.func("fa_decode_combine_gate_f32_s")
23546 } else {
23547 self.func("fa_decode_combine_gate_f32")
23548 }
23549 } else {
23550 self.fa_func("fa_decode_combine_f32", head_dim)
23551 };
23552 let cfg2 = LaunchConfig {
23553 grid_dim: (n_head as u32, 1, 1),
23554 block_dim: (head_dim as u32, 1, 1),
23555 shared_mem_bytes: if combine_shared {
23556 (2 * n_splits * 4) as u32
23557 } else {
23558 0
23559 },
23560 };
23561 let __s_b2 = self.gpu.stream();
23562 let mut b2 = __s_b2.launch_builder(&fc);
23563 b2.arg(&*part_o).arg(&*part_m).arg(&*part_l);
23564 if let Some(gate_row) = fused_gate {
23565 b2.arg(gate_row);
23566 }
23567 b2.arg(o).arg(&hd).arg(&nh).arg(&nsp);
23568 unsafe {
23569 b2.launch(cfg2)?;
23570 }
23571 Ok(())
23572 }
23573
23574 pub fn fa_geom_eager(
23580 &self,
23581 t_kv: usize,
23582 head_dim: usize,
23583 n_head_kv: usize,
23584 g: bool,
23585 ) -> (bool, usize) {
23586 let fa_ok = std::env::var("MEMRA_NO_FA_VEC").is_err() && t_kv >= fa_vec_min_tkv();
23590 let vec512 = fa_ok && head_dim == 512 && t_kv >= fa512_min_tkv();
23596 let mut fa_vec = vec512 || (fa_ok && head_dim <= 256 && head_dim % 32 == 0);
23597 if g && head_dim == 256 && !fa_v4_at(t_kv) {
23603 fa_vec = false;
23604 }
23605 let sp = fa_split_keys(t_kv, n_head_kv);
23606 let n_splits = if fa_vec {
23607 ((t_kv + sp - 1) / sp).max(1)
23608 } else {
23609 ((t_kv + 255) / 256).max(1)
23610 };
23611 (fa_vec, n_splits)
23612 }
23613
23614 pub fn fa_bucket_key(
23620 &self,
23621 t_kv: usize,
23622 head_dim: usize,
23623 n_head_kv: usize,
23624 g: bool,
23625 ) -> (bool, usize) {
23626 self.fa_geom_eager(t_kv, head_dim, n_head_kv, g)
23627 }
23628
23629 pub fn capture_graph_retained<F>(
23641 &self,
23642 step: F,
23643 ) -> Result<
23644 (
23645 cudarc::driver::CudaGraph,
23646 Vec<Box<dyn std::any::Any + Send>>,
23647 ),
23648 Box<dyn std::error::Error>,
23649 >
23650 where
23651 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
23652 {
23653 use cudarc::driver::sys::CUgraphInstantiate_flags;
23654 self.capture_graph_retained_flags(
23655 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
23656 step,
23657 )
23658 }
23659
23660 pub fn capture_graph_retained_flags<F>(
23665 &self,
23666 flags: cudarc::driver::sys::CUgraphInstantiate_flags,
23667 mut step: F,
23668 ) -> Result<
23669 (
23670 cudarc::driver::CudaGraph,
23671 Vec<Box<dyn std::any::Any + Send>>,
23672 ),
23673 Box<dyn std::error::Error>,
23674 >
23675 where
23676 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
23677 {
23678 use cudarc::driver::sys::CUstreamCaptureMode;
23679 self.capture_keep.lock().unwrap().clear();
23687 let was_tracking = self.gpu.ctx.is_event_tracking();
23688 if was_tracking {
23689 unsafe {
23690 self.gpu.ctx.disable_event_tracking();
23691 }
23692 }
23693 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
23694 self.capture_keep_on
23695 .store(true, std::sync::atomic::Ordering::Relaxed);
23696 let w = (|| {
23697 step(self)?;
23698 step(self)
23699 })();
23700 self.capture_keep_on
23701 .store(false, std::sync::atomic::Ordering::Relaxed);
23702 w?;
23703 self.gpu.stream().synchronize()?;
23704 self.gpu
23705 .stream()
23706 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
23707 let r = step(self);
23708 let g = self.gpu.stream().end_capture(flags);
23709 r?;
23710 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
23711 graph.upload()?;
23712 Ok(graph)
23713 };
23714 let result = run();
23715 self.capture_keep_on
23716 .store(false, std::sync::atomic::Ordering::Relaxed);
23717 if was_tracking {
23718 unsafe {
23719 self.gpu.ctx.enable_event_tracking();
23720 }
23721 }
23722 let keeper = std::mem::take(&mut *self.capture_keep.lock().unwrap());
23723 Ok((result?, keeper))
23724 }
23725
23726 pub fn capture_graph_retained_nowarm<F>(
23732 &self,
23733 mut step: F,
23734 ) -> Result<
23735 (
23736 cudarc::driver::CudaGraph,
23737 Vec<Box<dyn std::any::Any + Send>>,
23738 ),
23739 Box<dyn std::error::Error>,
23740 >
23741 where
23742 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
23743 {
23744 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
23745 let was_tracking = self.gpu.ctx.is_event_tracking();
23746 if was_tracking {
23747 unsafe {
23748 self.gpu.ctx.disable_event_tracking();
23749 }
23750 }
23751 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
23752 self.gpu.stream().synchronize()?;
23753 self.gpu
23754 .stream()
23755 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
23756 let r = step(self);
23757 let g = self.gpu.stream().end_capture(
23758 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
23759 );
23760 r?;
23761 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
23762 graph.upload()?;
23763 Ok(graph)
23764 };
23765 let result = run();
23766 if was_tracking {
23767 unsafe {
23768 self.gpu.ctx.enable_event_tracking();
23769 }
23770 }
23771 Ok((result?, Vec::new()))
23772 }
23773
23774 pub fn capture_graph<F>(
23775 &self,
23776 mut step: F,
23777 ) -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>>
23778 where
23779 F: FnMut(&Engine) -> Result<(), Box<dyn std::error::Error>>,
23780 {
23781 use cudarc::driver::sys::{CUgraphInstantiate_flags, CUstreamCaptureMode};
23782 let was_tracking = self.gpu.ctx.is_event_tracking();
23790 if was_tracking {
23791 unsafe {
23792 self.gpu.ctx.disable_event_tracking();
23793 }
23794 }
23795 let iflag = {
23802 static F: std::sync::OnceLock<CUgraphInstantiate_flags> = std::sync::OnceLock::new();
23803 *F.get_or_init(|| match std::env::var("MEMRA_GRAPH_IFLAG").as_deref() {
23804 Ok("upload") => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_UPLOAD,
23807 Ok("priority") => {
23808 CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_USE_NODE_PRIORITY
23809 }
23810 _ => CUgraphInstantiate_flags::CUDA_GRAPH_INSTANTIATE_FLAG_AUTO_FREE_ON_LAUNCH,
23811 })
23812 };
23813 let ct = {
23820 static T: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
23821 *T.get_or_init(|| std::env::var("MEMRA_GRAPH_CAPTIME").as_deref() == Ok("1"))
23822 };
23823 let warmups = {
23846 static W: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
23847 *W.get_or_init(|| {
23848 std::env::var("MEMRA_GRAPH_WARMUPS")
23849 .ok()
23850 .and_then(|v| v.parse().ok())
23851 .filter(|n| *n >= 1)
23852 .unwrap_or(1)
23853 })
23854 };
23855 let mut run = || -> Result<cudarc::driver::CudaGraph, Box<dyn std::error::Error>> {
23856 let t_w = std::time::Instant::now();
23857 for _ in 0..warmups {
23859 step(self)?;
23860 }
23861 self.gpu.stream().synchronize()?;
23862 let ms_warm = t_w.elapsed().as_secs_f64() * 1e3;
23863 let t_c = std::time::Instant::now();
23865 self.gpu
23866 .stream()
23867 .begin_capture(CUstreamCaptureMode::CU_STREAM_CAPTURE_MODE_RELAXED)?;
23868 let r = step(self);
23871 let ms_body = t_c.elapsed().as_secs_f64() * 1e3;
23872 let t_i = std::time::Instant::now();
23873 let g = self.gpu.stream().end_capture(iflag);
23874 let ms_inst = t_i.elapsed().as_secs_f64() * 1e3;
23875 r?;
23876 let graph = g?.ok_or("capture produced no graph (stream was not capturing)")?;
23877 let t_u = std::time::Instant::now();
23878 graph.upload()?;
23879 if ct {
23880 println!(
23881 "[graph-captime] warmup2x {ms_warm:.2} ms capture-body {ms_body:.2} ms \
23882 instantiate {ms_inst:.2} ms upload {:.2} ms",
23883 t_u.elapsed().as_secs_f64() * 1e3
23884 );
23885 }
23886 Ok(graph)
23887 };
23888 let result = run();
23889 if was_tracking {
23890 unsafe {
23891 self.gpu.ctx.enable_event_tracking();
23892 }
23893 }
23894 result
23895 }
23896
23897 pub fn gdn_scan_s128_view(
23899 &self,
23900 q: &CudaSlice<f32>,
23901 k: &CudaSlice<f32>,
23902 v: &CudaSlice<f32>,
23903 g: &CudaSlice<f32>,
23904 beta: &CudaSlice<f32>,
23905 state_in: &cudarc::driver::CudaView<f32>,
23906 state_out: &mut cudarc::driver::CudaViewMut<f32>,
23907 o: &mut CudaSlice<f32>,
23908 n_head: usize,
23909 t: usize,
23910 scale: f32,
23911 ) -> Result<(), Box<dyn std::error::Error>> {
23912 let f = self.func("gdn_scan_s128");
23913 const S_V: u32 = 128;
23914 const WARP: u32 = 32;
23915 const COLS: u32 = 4;
23916 let cfg = LaunchConfig {
23917 grid_dim: (n_head as u32, 1, S_V / COLS),
23918 block_dim: (WARP, COLS, 1),
23919 shared_mem_bytes: 0,
23920 };
23921 let (h, ti) = (n_head as i32, t as i32);
23922 let __s_b = self.gpu.stream();
23923 let mut b = __s_b.launch_builder(&f);
23924 b.arg(q)
23925 .arg(k)
23926 .arg(v)
23927 .arg(g)
23928 .arg(beta)
23929 .arg(state_in)
23930 .arg(state_out)
23931 .arg(o)
23932 .arg(&h)
23933 .arg(&ti)
23934 .arg(&scale);
23935 unsafe {
23936 b.launch(cfg)?;
23937 }
23938 Ok(())
23939 }
23940
23941 pub fn ssm_conv1d_view(
23943 &self,
23944 x: &cudarc::driver::CudaView<f32>,
23945 w: &CudaSlice<f32>,
23946 y: &mut CudaSlice<f32>,
23947 conv_dim: usize,
23948 t: usize,
23949 d_conv: usize,
23950 silu: bool,
23951 ) -> Result<(), Box<dyn std::error::Error>> {
23952 let f = self.func("ssm_conv1d_silu_f32");
23953 let cfg = LaunchConfig {
23955 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
23956 block_dim: (256, 1, 1),
23957 shared_mem_bytes: 0,
23958 };
23959 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
23960 let __s_b = self.gpu.stream();
23961 let mut b = __s_b.launch_builder(&f);
23962 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
23963 unsafe {
23964 b.launch(cfg)?;
23965 }
23966 Ok(())
23967 }
23968
23969 pub fn ssm_conv1d_tm(
23976 &self,
23977 qkv_tm: &CudaSlice<f32>,
23978 w: &CudaSlice<f32>,
23979 y: &mut CudaSlice<f32>,
23980 conv_dim: usize,
23981 t: usize,
23982 d_conv: usize,
23983 ) -> Result<(), Box<dyn std::error::Error>> {
23984 let f = self.func("ssm_conv1d_tm_f32");
23985 let cfg = LaunchConfig {
23986 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
23987 block_dim: (256, 1, 1),
23988 shared_mem_bytes: 0,
23989 };
23990 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
23991 let __s_b = self.gpu.stream();
23992 let mut b = __s_b.launch_builder(&f);
23993 b.arg(qkv_tm).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc);
23994 unsafe {
23995 b.launch(cfg)?;
23996 }
23997 Ok(())
23998 }
23999
24000 pub fn ssm_conv1d_tm_state(
24008 &self,
24009 qkv_tm: &CudaSlice<f32>,
24010 conv_state: &mut CudaSlice<f32>,
24011 w: &CudaSlice<f32>,
24012 y: &mut CudaSlice<f32>,
24013 conv_dim: usize,
24014 t: usize,
24015 d_conv: usize,
24016 ) -> Result<(), Box<dyn std::error::Error>> {
24017 self.ssm_conv1d_tm_state_pad(qkv_tm, conv_state, w, y, conv_dim, t, d_conv, None)
24018 }
24019
24020 #[allow(clippy::too_many_arguments)]
24023 pub fn ssm_conv1d_tm_state_pad(
24024 &self,
24025 qkv_tm: &CudaSlice<f32>,
24026 conv_state: &mut CudaSlice<f32>,
24027 w: &CudaSlice<f32>,
24028 y: &mut CudaSlice<f32>,
24029 conv_dim: usize,
24030 t: usize,
24031 d_conv: usize,
24032 pad_len: Option<&CudaSlice<i32>>,
24033 ) -> Result<(), Box<dyn std::error::Error>> {
24034 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
24035 let ring_old = if t < d_conv - 1 {
24039 Some(self.clone_dtod(conv_state)?)
24040 } else {
24041 None
24042 };
24043 {
24044 let f = self.func("ssm_conv1d_tm_state_f32");
24045 let cfg = LaunchConfig {
24046 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
24047 block_dim: (256, 1, 1),
24048 shared_mem_bytes: 0,
24049 };
24050 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
24051 let __s_b = self.gpu.stream();
24052 let mut b = __s_b.launch_builder(&f);
24053 b.arg(qkv_tm)
24054 .arg(&*conv_state)
24055 .arg(w)
24056 .arg(y)
24057 .arg(&cd)
24058 .arg(&ti)
24059 .arg(&dc);
24060 unsafe {
24061 b.launch(cfg)?;
24062 }
24063 }
24064 match (ring_old, pad_len) {
24065 (None, Some(len_d)) => {
24066 let f = self.func("ssm_conv_ring_update_dev_f32");
24067 let n = conv_dim * (d_conv - 1);
24068 let cfg = LaunchConfig::for_num_elems(n as u32);
24069 let (cd, dc) = (conv_dim as i32, d_conv as i32);
24070 let __s_b = self.gpu.stream();
24071 let mut b = __s_b.launch_builder(&f);
24072 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
24073 unsafe {
24074 b.launch(cfg)?;
24075 }
24076 }
24077 (None, None) => {
24078 let f = self.func("ssm_conv_ring_update_f32");
24079 let n = conv_dim * (d_conv - 1);
24080 let cfg = LaunchConfig::for_num_elems(n as u32);
24081 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
24082 let __s_b = self.gpu.stream();
24083 let mut b = __s_b.launch_builder(&f);
24084 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
24085 unsafe {
24086 b.launch(cfg)?;
24087 }
24088 }
24089 (Some(old), _) => {
24090 self.ssm_conv_ring_rebuild(qkv_tm, &old, conv_state, conv_dim, t, d_conv)?
24091 }
24092 }
24093 Ok(())
24094 }
24095
24096 pub fn ssm_conv1d_tm_state_pad_v(
24098 &self,
24099 qkv_tm: &cudarc::driver::CudaView<f32>,
24100 conv_state: &mut CudaSlice<f32>,
24101 w: &CudaSlice<f32>,
24102 y: &mut CudaSlice<f32>,
24103 conv_dim: usize,
24104 t: usize,
24105 d_conv: usize,
24106 pad_len: Option<&CudaSlice<i32>>,
24107 ) -> Result<(), Box<dyn std::error::Error>> {
24108 assert!(t >= 1, "ssm_conv1d_tm_state requires T >= 1");
24109 let ring_old = if t < d_conv - 1 {
24113 Some(self.clone_dtod(conv_state)?)
24114 } else {
24115 None
24116 };
24117 {
24118 let f = self.func("ssm_conv1d_tm_state_f32");
24119 let cfg = LaunchConfig {
24120 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
24121 block_dim: (256, 1, 1),
24122 shared_mem_bytes: 0,
24123 };
24124 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
24125 let __s_b = self.gpu.stream();
24126 let mut b = __s_b.launch_builder(&f);
24127 b.arg(qkv_tm)
24128 .arg(&*conv_state)
24129 .arg(w)
24130 .arg(y)
24131 .arg(&cd)
24132 .arg(&ti)
24133 .arg(&dc);
24134 unsafe {
24135 b.launch(cfg)?;
24136 }
24137 }
24138 match (ring_old, pad_len) {
24139 (None, Some(len_d)) => {
24140 let f = self.func("ssm_conv_ring_update_dev_f32");
24141 let n = conv_dim * (d_conv - 1);
24142 let cfg = LaunchConfig::for_num_elems(n as u32);
24143 let (cd, dc) = (conv_dim as i32, d_conv as i32);
24144 let __s_b = self.gpu.stream();
24145 let mut b = __s_b.launch_builder(&f);
24146 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
24147 unsafe {
24148 b.launch(cfg)?;
24149 }
24150 }
24151 (None, None) => {
24152 let f = self.func("ssm_conv_ring_update_f32");
24153 let n = conv_dim * (d_conv - 1);
24154 let cfg = LaunchConfig::for_num_elems(n as u32);
24155 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
24156 let __s_b = self.gpu.stream();
24157 let mut b = __s_b.launch_builder(&f);
24158 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
24159 unsafe {
24160 b.launch(cfg)?;
24161 }
24162 }
24163 (Some(_), _) => unreachable!(
24164 "ssm_conv1d_tm_state_pad_v: T < d_conv-1 has no view path (PRIME_MIN_T gates it)"
24165 ),
24166 }
24167 Ok(())
24168 }
24169
24170 pub fn ssm_conv_ring_rebuild(
24175 &self,
24176 qkv_tm: &CudaSlice<f32>,
24177 ring_old: &CudaSlice<f32>,
24178 conv_state: &mut CudaSlice<f32>,
24179 conv_dim: usize,
24180 tc: usize,
24181 d_conv: usize,
24182 ) -> Result<(), Box<dyn std::error::Error>> {
24183 let f = self.func("ssm_conv_ring_rebuild_f32");
24184 let n = conv_dim * (d_conv - 1);
24185 let cfg = LaunchConfig::for_num_elems(n as u32);
24186 let (cd, ti, dc) = (conv_dim as i32, tc as i32, d_conv as i32);
24187 let __s_b = self.gpu.stream();
24188 let mut b = __s_b.launch_builder(&f);
24189 b.arg(qkv_tm)
24190 .arg(ring_old)
24191 .arg(conv_state)
24192 .arg(&cd)
24193 .arg(&ti)
24194 .arg(&dc);
24195 unsafe {
24196 b.launch(cfg)?;
24197 }
24198 Ok(())
24199 }
24200
24201 #[allow(clippy::too_many_arguments)]
24206 pub fn gdn_prep_decode(
24207 &self,
24208 conv_out: &CudaSlice<f32>,
24209 beta_raw: &CudaSlice<f32>,
24210 alpha: &CudaSlice<f32>,
24211 dt_bias: &CudaSlice<f32>,
24212 a: &CudaSlice<f32>,
24213 q_l2: &mut CudaSlice<f32>,
24214 k_l2: &mut CudaSlice<f32>,
24215 v_g: &mut CudaSlice<f32>,
24216 beta: &mut CudaSlice<f32>,
24217 g_log: &mut CudaSlice<f32>,
24218 d_state: usize,
24219 num_v: usize,
24220 num_k: usize,
24221 key_dim: usize,
24222 eps: f32,
24223 ) -> Result<(), Box<dyn std::error::Error>> {
24224 let f = self.func("gdn_prep_decode_f32");
24225 let cfg = LaunchConfig {
24226 grid_dim: (num_v as u32, 1, 1),
24227 block_dim: (32, 4, 1),
24228 shared_mem_bytes: 0,
24229 };
24230 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
24231 let __s_b = self.gpu.stream();
24232 let mut b = __s_b.launch_builder(&f);
24233 b.arg(conv_out)
24234 .arg(beta_raw)
24235 .arg(alpha)
24236 .arg(dt_bias)
24237 .arg(a)
24238 .arg(q_l2)
24239 .arg(k_l2)
24240 .arg(v_g)
24241 .arg(beta)
24242 .arg(g_log)
24243 .arg(&ds)
24244 .arg(&nv)
24245 .arg(&nk)
24246 .arg(&kd)
24247 .arg(&eps);
24248 unsafe {
24249 b.launch(cfg)?;
24250 }
24251 Ok(())
24252 }
24253
24254 #[allow(clippy::too_many_arguments)]
24258 pub fn ssm_conv1d_gdn(
24259 &self,
24260 qkv_tm: &CudaSlice<f32>,
24261 w: &CudaSlice<f32>,
24262 q_g: &mut CudaSlice<f32>,
24263 k_g: &mut CudaSlice<f32>,
24264 v_g: &mut CudaSlice<f32>,
24265 conv_dim: usize,
24266 t: usize,
24267 d_conv: usize,
24268 d_state: usize,
24269 num_v: usize,
24270 num_k: usize,
24271 key_dim: usize,
24272 ) -> Result<(), Box<dyn std::error::Error>> {
24273 let f = self.func("ssm_conv1d_gdn_f32");
24274 let cfg = LaunchConfig {
24275 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
24276 block_dim: (256, 1, 1),
24277 shared_mem_bytes: 0,
24278 };
24279 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
24280 let (ds, nv, nk, kd) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
24281 let __s_b = self.gpu.stream();
24282 let mut b = __s_b.launch_builder(&f);
24283 b.arg(qkv_tm)
24284 .arg(w)
24285 .arg(q_g)
24286 .arg(k_g)
24287 .arg(v_g)
24288 .arg(&cd)
24289 .arg(&ti)
24290 .arg(&dc)
24291 .arg(&ds)
24292 .arg(&nv)
24293 .arg(&nk)
24294 .arg(&kd);
24295 unsafe {
24296 b.launch(cfg)?;
24297 }
24298 Ok(())
24299 }
24300
24301 pub fn ssm_conv1d(
24302 &self,
24303 x: &CudaSlice<f32>,
24304 w: &CudaSlice<f32>,
24305 y: &mut CudaSlice<f32>,
24306 conv_dim: usize,
24307 t: usize,
24308 d_conv: usize,
24309 silu: bool,
24310 ) -> Result<(), Box<dyn std::error::Error>> {
24311 let f = self.func("ssm_conv1d_silu_f32");
24312 let cfg = LaunchConfig {
24313 grid_dim: (conv_dim as u32, ((t as u32 + 255) / 256).max(1), 1),
24314 block_dim: (256, 1, 1),
24315 shared_mem_bytes: 0,
24316 };
24317 let (cd, ti, dc, s) = (conv_dim as i32, t as i32, d_conv as i32, silu as i32);
24318 let __s_b = self.gpu.stream();
24319 let mut b = __s_b.launch_builder(&f);
24320 b.arg(x).arg(w).arg(y).arg(&cd).arg(&ti).arg(&dc).arg(&s);
24321 unsafe {
24322 b.launch(cfg)?;
24323 }
24324 Ok(())
24325 }
24326
24327 pub fn gdn_scan_s128(
24330 &self,
24331 q: &CudaSlice<f32>,
24332 k: &CudaSlice<f32>,
24333 v: &CudaSlice<f32>,
24334 g: &CudaSlice<f32>,
24335 beta: &CudaSlice<f32>,
24336 state_in: &CudaSlice<f32>,
24337 state_out: &mut CudaSlice<f32>,
24338 o: &mut CudaSlice<f32>,
24339 n_head: usize,
24340 t: usize,
24341 scale: f32,
24342 ) -> Result<(), Box<dyn std::error::Error>> {
24343 let f = self.func("gdn_scan_s128");
24344 const S_V: u32 = 128;
24345 const WARP: u32 = 32;
24346 const COLS_PER_BLOCK: u32 = 4;
24347 let cfg = LaunchConfig {
24348 grid_dim: (n_head as u32, 1, S_V / COLS_PER_BLOCK),
24349 block_dim: (WARP, COLS_PER_BLOCK, 1),
24350 shared_mem_bytes: 0,
24351 };
24352 let (h, ti) = (n_head as i32, t as i32);
24353 let __s_b = self.gpu.stream();
24354 let mut b = __s_b.launch_builder(&f);
24355 b.arg(q)
24356 .arg(k)
24357 .arg(v)
24358 .arg(g)
24359 .arg(beta)
24360 .arg(state_in)
24361 .arg(state_out)
24362 .arg(o)
24363 .arg(&h)
24364 .arg(&ti)
24365 .arg(&scale);
24366 unsafe {
24367 b.launch(cfg)?;
24368 }
24369 Ok(())
24370 }
24371
24372 #[allow(clippy::too_many_arguments)]
24377 pub fn ssm_conv1d_fused_decode_b(
24378 &self,
24379 qkv_cols: &CudaSlice<f32>,
24380 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
24381 w: &CudaSlice<f32>,
24382 conv_outs: &mut CudaSlice<f32>,
24383 conv_dim: usize,
24384 d_conv: usize,
24385 b_n: usize,
24386 ) -> Result<(), Box<dyn std::error::Error>> {
24387 let f = self.func("ssm_conv1d_fused_decode_b_f32");
24388 let cfg = LaunchConfig {
24389 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
24390 block_dim: (256, 1, 1),
24391 shared_mem_bytes: 0,
24392 };
24393 let (cd, dc) = (conv_dim as i32, d_conv as i32);
24394 let __s_b = self.gpu.stream();
24395 let mut b = __s_b.launch_builder(&f);
24396 b.arg(qkv_cols)
24397 .arg(conv_state_ptrs)
24398 .arg(w)
24399 .arg(conv_outs)
24400 .arg(&cd)
24401 .arg(&dc);
24402 unsafe {
24403 b.launch(cfg)?;
24404 }
24405 Ok(())
24406 }
24407
24408 #[allow(clippy::too_many_arguments)]
24409 pub fn gdn_prep_decode_b(
24410 &self,
24411 conv_outs: &CudaSlice<f32>,
24412 beta_raws: &CudaSlice<f32>,
24413 alphas: &CudaSlice<f32>,
24414 dt_bias: &CudaSlice<f32>,
24415 a: &CudaSlice<f32>,
24416 q_l2: &mut CudaSlice<f32>,
24417 k_l2: &mut CudaSlice<f32>,
24418 v_g: &mut CudaSlice<f32>,
24419 beta: &mut CudaSlice<f32>,
24420 g_log: &mut CudaSlice<f32>,
24421 d_state: usize,
24422 num_v: usize,
24423 num_k: usize,
24424 key_dim: usize,
24425 eps: f32,
24426 conv_dim: usize,
24427 b_n: usize,
24428 ) -> Result<(), Box<dyn std::error::Error>> {
24429 let f = self.func("gdn_prep_decode_b_f32");
24430 let cfg = LaunchConfig {
24431 grid_dim: (num_v as u32, 1, b_n as u32),
24432 block_dim: (32, 4, 1),
24433 shared_mem_bytes: 0,
24434 };
24435 let (ds, nv, nk, kd, cd) = (
24436 d_state as i32,
24437 num_v as i32,
24438 num_k as i32,
24439 key_dim as i32,
24440 conv_dim as i32,
24441 );
24442 let __s_b = self.gpu.stream();
24443 let mut b = __s_b.launch_builder(&f);
24444 b.arg(conv_outs)
24445 .arg(beta_raws)
24446 .arg(alphas)
24447 .arg(dt_bias)
24448 .arg(a)
24449 .arg(q_l2)
24450 .arg(k_l2)
24451 .arg(v_g)
24452 .arg(beta)
24453 .arg(g_log)
24454 .arg(&ds)
24455 .arg(&nv)
24456 .arg(&nk)
24457 .arg(&kd)
24458 .arg(&eps)
24459 .arg(&cd);
24460 unsafe {
24461 b.launch(cfg)?;
24462 }
24463 Ok(())
24464 }
24465
24466 #[allow(clippy::too_many_arguments)]
24467 pub fn gdn_scan_s128_batched(
24468 &self,
24469 q: &CudaSlice<f32>,
24470 k: &CudaSlice<f32>,
24471 v: &CudaSlice<f32>,
24472 g: &CudaSlice<f32>,
24473 beta: &CudaSlice<f32>,
24474 state_in_ptrs: &cudarc::driver::CudaView<u64>,
24475 state_out_ptrs: &cudarc::driver::CudaView<u64>,
24476 o: &mut CudaSlice<f32>,
24477 n_head: usize,
24478 b_n: usize,
24479 scale: f32,
24480 ) -> Result<(), Box<dyn std::error::Error>> {
24481 let f = self.func("gdn_scan_s128_b");
24482 const S_V: u32 = 128;
24483 const WARP: u32 = 32;
24484 const COLS_PER_BLOCK: u32 = 4;
24485 let cfg = LaunchConfig {
24486 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
24487 block_dim: (WARP, COLS_PER_BLOCK, 1),
24488 shared_mem_bytes: 0,
24489 };
24490 let h = n_head as i32;
24491 let __s_b = self.gpu.stream();
24492 let mut b = __s_b.launch_builder(&f);
24493 b.arg(q)
24494 .arg(k)
24495 .arg(v)
24496 .arg(g)
24497 .arg(beta)
24498 .arg(state_in_ptrs)
24499 .arg(state_out_ptrs)
24500 .arg(o)
24501 .arg(&h)
24502 .arg(&scale);
24503 unsafe {
24504 b.launch(cfg)?;
24505 }
24506 Ok(())
24507 }
24508
24509 #[allow(clippy::too_many_arguments)]
24515 pub fn ssm_conv1d_fused_decode_b_view(
24516 &self,
24517 qkv_cols: &cudarc::driver::CudaView<f32>,
24518 conv_state_ptrs: &cudarc::driver::CudaView<u64>,
24519 w: &CudaSlice<f32>,
24520 conv_outs: &mut CudaSlice<f32>,
24521 conv_dim: usize,
24522 d_conv: usize,
24523 b_n: usize,
24524 ) -> Result<(), Box<dyn std::error::Error>> {
24525 let f = self.func("ssm_conv1d_fused_decode_b_f32");
24526 let cfg = LaunchConfig {
24527 grid_dim: (((conv_dim + 255) / 256) as u32, 1, b_n as u32),
24528 block_dim: (256, 1, 1),
24529 shared_mem_bytes: 0,
24530 };
24531 let (cd, dc) = (conv_dim as i32, d_conv as i32);
24532 let __s_b = self.gpu.stream();
24533 let mut b = __s_b.launch_builder(&f);
24534 b.arg(qkv_cols)
24535 .arg(conv_state_ptrs)
24536 .arg(w)
24537 .arg(conv_outs)
24538 .arg(&cd)
24539 .arg(&dc);
24540 unsafe {
24541 b.launch(cfg)?;
24542 }
24543 Ok(())
24544 }
24545
24546 #[allow(clippy::too_many_arguments)]
24547 pub fn gdn_prep_decode_b_view(
24548 &self,
24549 conv_outs: &CudaSlice<f32>,
24550 beta_raws: &cudarc::driver::CudaView<f32>,
24551 alphas: &cudarc::driver::CudaView<f32>,
24552 dt_bias: &CudaSlice<f32>,
24553 a: &CudaSlice<f32>,
24554 q_l2: &mut CudaSlice<f32>,
24555 k_l2: &mut CudaSlice<f32>,
24556 v_g: &mut CudaSlice<f32>,
24557 beta: &mut CudaSlice<f32>,
24558 g_log: &mut CudaSlice<f32>,
24559 d_state: usize,
24560 num_v: usize,
24561 num_k: usize,
24562 key_dim: usize,
24563 eps: f32,
24564 conv_dim: usize,
24565 b_n: usize,
24566 ) -> Result<(), Box<dyn std::error::Error>> {
24567 let f = self.func("gdn_prep_decode_b_f32");
24568 let cfg = LaunchConfig {
24569 grid_dim: (num_v as u32, 1, b_n as u32),
24570 block_dim: (32, 4, 1),
24571 shared_mem_bytes: 0,
24572 };
24573 let (ds, nv, nk, kd, cd) = (
24574 d_state as i32,
24575 num_v as i32,
24576 num_k as i32,
24577 key_dim as i32,
24578 conv_dim as i32,
24579 );
24580 let __s_b = self.gpu.stream();
24581 let mut b = __s_b.launch_builder(&f);
24582 b.arg(conv_outs)
24583 .arg(beta_raws)
24584 .arg(alphas)
24585 .arg(dt_bias)
24586 .arg(a)
24587 .arg(q_l2)
24588 .arg(k_l2)
24589 .arg(v_g)
24590 .arg(beta)
24591 .arg(g_log)
24592 .arg(&ds)
24593 .arg(&nv)
24594 .arg(&nk)
24595 .arg(&kd)
24596 .arg(&eps)
24597 .arg(&cd);
24598 unsafe {
24599 b.launch(cfg)?;
24600 }
24601 Ok(())
24602 }
24603
24604 #[allow(clippy::too_many_arguments)]
24605 pub fn gdn_scan_s128_batched_view(
24606 &self,
24607 q: &CudaSlice<f32>,
24608 k: &CudaSlice<f32>,
24609 v: &CudaSlice<f32>,
24610 g: &CudaSlice<f32>,
24611 beta: &CudaSlice<f32>,
24612 state_in_ptrs: &cudarc::driver::CudaView<u64>,
24613 state_out_ptrs: &cudarc::driver::CudaView<u64>,
24614 o: &mut cudarc::driver::CudaViewMut<f32>,
24615 n_head: usize,
24616 b_n: usize,
24617 scale: f32,
24618 ) -> Result<(), Box<dyn std::error::Error>> {
24619 let f = self.func("gdn_scan_s128_b");
24620 const S_V: u32 = 128;
24621 const WARP: u32 = 32;
24622 const COLS_PER_BLOCK: u32 = 4;
24623 let cfg = LaunchConfig {
24624 grid_dim: (n_head as u32, b_n as u32, S_V / COLS_PER_BLOCK),
24625 block_dim: (WARP, COLS_PER_BLOCK, 1),
24626 shared_mem_bytes: 0,
24627 };
24628 let h = n_head as i32;
24629 let __s_b = self.gpu.stream();
24630 let mut b = __s_b.launch_builder(&f);
24631 b.arg(q)
24632 .arg(k)
24633 .arg(v)
24634 .arg(g)
24635 .arg(beta)
24636 .arg(state_in_ptrs)
24637 .arg(state_out_ptrs)
24638 .arg(o)
24639 .arg(&h)
24640 .arg(&scale);
24641 unsafe {
24642 b.launch(cfg)?;
24643 }
24644 Ok(())
24645 }
24646
24647 pub fn gdn_chunked_enabled() -> bool {
24656 static E: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
24657 *E.get_or_init(|| {
24658 std::env::var("MEMRA_GDN_CHUNKED")
24659 .map(|v| v != "0")
24660 .unwrap_or(true)
24661 })
24662 }
24663
24664 pub fn gdn_chunk_size() -> usize {
24669 static C: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
24670 *C.get_or_init(|| {
24671 let c: usize = std::env::var("MEMRA_GDN_CHUNK")
24672 .ok()
24673 .and_then(|v| v.parse().ok())
24674 .unwrap_or(32);
24675 c.clamp(32, 128) / 32 * 32
24676 })
24677 }
24678
24679 #[allow(clippy::too_many_arguments)]
24684 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
24687 #[allow(clippy::too_many_arguments)]
24688 pub fn gdn_chunk_k123(
24689 &self,
24690 q: &CudaSlice<f32>,
24691 k: &CudaSlice<f32>,
24692 v: &CudaSlice<f32>,
24693 g: &CudaSlice<f32>,
24694 beta: &CudaSlice<f32>,
24695 wb16: Option<&mut CudaSlice<u8>>,
24696 n_head: usize,
24697 t: usize,
24698 c: usize,
24699 hk: usize,
24700 k2w: Option<(&CudaSlice<u8>, &CudaSlice<u8>, &mut CudaSlice<u8>)>,
24701 ) -> Result<
24702 (
24703 CudaSlice<f32>,
24704 CudaSlice<f32>,
24705 CudaSlice<f32>,
24706 CudaSlice<f32>,
24707 ),
24708 Box<dyn std::error::Error>,
24709 > {
24710 const D: usize = 128;
24711 let h = n_head;
24712 let nc = (t + c - 1) / c;
24713 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
24714 let mut gcum = self.uninit(t * h)?;
24715 let mut a = self.uninit(nc * h * c * c)?;
24716 let mut p = self.uninit(nc * h * c * c)?;
24717 let mut u = self.uninit(nc * h * c * D)?;
24718 let mut w = self.uninit(nc * h * c * D)?;
24719 {
24720 let f = self.func("gdn_chunk_cumgate_f32");
24722 let cfg = LaunchConfig {
24723 grid_dim: (nc as u32, h as u32, 1),
24724 block_dim: (32, 1, 1),
24725 shared_mem_bytes: 0,
24726 };
24727 let __s_b = self.gpu.stream();
24728 let mut b = __s_b.launch_builder(&f);
24729 b.arg(g).arg(&mut gcum).arg(&hi).arg(&ti).arg(&ci);
24730 unsafe {
24731 b.launch(cfg)?;
24732 }
24733 }
24734 if let Some((qb, kb, pb)) = k2w {
24735 assert!(c == 32, "gdn_k2_wgmma is a C==32 tile");
24738 let f = self.func("gdn_k2_wgmma");
24739 let cfg = LaunchConfig {
24740 grid_dim: (nc as u32, h as u32, 1),
24741 block_dim: (128, 1, 1),
24742 shared_mem_bytes: 0,
24743 };
24744 let hki = hk as i32;
24745 let __s_b = self.gpu.stream();
24746 let mut b = __s_b.launch_builder(&f);
24747 b.arg(qb)
24748 .arg(kb)
24749 .arg(&gcum)
24750 .arg(beta)
24751 .arg(&mut a)
24752 .arg(&mut *pb)
24753 .arg(&hi)
24754 .arg(&ti)
24755 .arg(&ci)
24756 .arg(&hki);
24757 unsafe {
24758 b.launch(cfg)?;
24759 }
24760 } else if c <= 64 && !portable_mma_gated() {
24761 let f = self.func("gdn_chunk_attn_f32");
24763 f.set_attribute(
24764 CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
24765 GDN_K2_DYNAMIC_SHARED_BYTES as i32,
24766 )?;
24767 let jt = ((c + 31) / 32) as u32;
24768 let cfg = LaunchConfig {
24769 grid_dim: (nc as u32, h as u32, jt),
24770 block_dim: (256, 1, 1),
24771 shared_mem_bytes: GDN_K2_DYNAMIC_SHARED_BYTES,
24772 };
24773 let hki = hk as i32;
24774 let __s_b = self.gpu.stream();
24775 let mut b = __s_b.launch_builder(&f);
24776 b.arg(q)
24777 .arg(k)
24778 .arg(&gcum)
24779 .arg(beta)
24780 .arg(&mut a)
24781 .arg(&mut p)
24782 .arg(&hi)
24783 .arg(&ti)
24784 .arg(&ci)
24785 .arg(&hki);
24786 unsafe {
24787 b.launch(cfg)?;
24788 }
24789 } else {
24790 assert!(
24792 hk == h,
24793 "generic K2 is broadcast-only (de-broadcast rides C==32)"
24794 );
24795 let f = self.func("gdn_chunk_attn_g_f32");
24796 let cfg = LaunchConfig {
24797 grid_dim: (nc as u32, h as u32, 1),
24798 block_dim: (32, 8, 1),
24799 shared_mem_bytes: 0,
24800 };
24801 let __s_b = self.gpu.stream();
24802 let mut b = __s_b.launch_builder(&f);
24803 b.arg(q)
24804 .arg(k)
24805 .arg(&gcum)
24806 .arg(beta)
24807 .arg(&mut a)
24808 .arg(&mut p)
24809 .arg(&hi)
24810 .arg(&ti)
24811 .arg(&ci);
24812 unsafe {
24813 b.launch(cfg)?;
24814 }
24815 }
24816 {
24817 let cfg = LaunchConfig {
24819 grid_dim: (nc as u32, h as u32, 1),
24820 block_dim: (256, 1, 1),
24821 shared_mem_bytes: 0,
24822 };
24823 match c {
24824 32 | 64 => {
24825 let f = self.func(if c == 32 {
24826 "gdn_chunk_solve32_f32"
24827 } else {
24828 "gdn_chunk_solve64_f32"
24829 });
24830 let wb: u64 = match wb16 {
24832 Some(d) => self.addr_u8(d),
24833 None => 0,
24834 };
24835 let hki = hk as i32;
24836 let __s_b = self.gpu.stream();
24837 let mut b = __s_b.launch_builder(&f);
24838 b.arg(v)
24839 .arg(k)
24840 .arg(&a)
24841 .arg(&gcum)
24842 .arg(&mut u)
24843 .arg(&mut w)
24844 .arg(&wb)
24845 .arg(&hi)
24846 .arg(&ti)
24847 .arg(&hki);
24848 unsafe {
24849 b.launch(cfg)?;
24850 }
24851 }
24852 _ => {
24853 assert!(hk == h, "generic K3 is broadcast-only");
24854 let f = self.func("gdn_chunk_solve_f32");
24855 let __s_b = self.gpu.stream();
24856 let mut b = __s_b.launch_builder(&f);
24857 b.arg(v)
24858 .arg(k)
24859 .arg(&a)
24860 .arg(&gcum)
24861 .arg(&mut u)
24862 .arg(&mut w)
24863 .arg(&hi)
24864 .arg(&ti)
24865 .arg(&ci);
24866 unsafe {
24867 b.launch(cfg)?;
24868 }
24869 }
24870 }
24871 }
24872 Ok((gcum, p, u, w))
24873 }
24874
24875 pub fn gdn_db_on() -> bool {
24879 std::env::var("MEMRA_GDN_DB").as_deref() != Ok("0")
24880 }
24881
24882 pub fn gdn_mma_enabled(&self, c: usize) -> bool {
24891 !portable_mma_gated()
24892 && c == 32
24893 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
24894 Ok("1") => true,
24895 Ok("0") => false,
24896 _ => gdn_mma_default_on(),
24897 }
24898 }
24899
24900 pub fn gdn_wgmma_on(&self, c: usize) -> bool {
24907 cfg!(memra_hopper_mma)
24908 && self.gdn_mma_enabled(c)
24909 && std::env::var("MEMRA_GDN_WGMMA").as_deref() != Ok("0")
24910 }
24911
24912 #[allow(clippy::too_many_arguments)]
24917 pub fn ssm_conv1d_gdn_state_pad(
24918 &self,
24919 qkv_tm: &cudarc::driver::CudaView<f32>,
24920 conv_state: &mut CudaSlice<f32>,
24921 w: &CudaSlice<f32>,
24922 q_g: &mut CudaSlice<f32>,
24923 k_g: &mut CudaSlice<f32>,
24924 v_g: &mut CudaSlice<f32>,
24925 conv_dim: usize,
24926 t: usize,
24927 d_conv: usize,
24928 d_state: usize,
24929 num_v: usize,
24930 num_k: usize,
24931 key_dim: usize,
24932 hk: usize,
24933 pad_len: Option<&CudaSlice<i32>>,
24934 ) -> Result<(), Box<dyn std::error::Error>> {
24935 assert!(
24936 t >= d_conv - 1,
24937 "fused state conv requires T >= pad (PRIME_MIN_T gates)"
24938 );
24939 {
24940 let f = self.func("ssm_conv1d_gdn_state_f32");
24941 let cfg = LaunchConfig {
24942 grid_dim: (((conv_dim + 255) / 256) as u32, t as u32, 1),
24943 block_dim: (256, 1, 1),
24944 shared_mem_bytes: 0,
24945 };
24946 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
24947 let (ds, nv, nk, kd, hki) = (
24948 d_state as i32,
24949 num_v as i32,
24950 num_k as i32,
24951 key_dim as i32,
24952 hk as i32,
24953 );
24954 let __s_b = self.gpu.stream();
24955 let mut b = __s_b.launch_builder(&f);
24956 b.arg(qkv_tm)
24957 .arg(&*conv_state)
24958 .arg(w)
24959 .arg(q_g)
24960 .arg(k_g)
24961 .arg(v_g)
24962 .arg(&cd)
24963 .arg(&ti)
24964 .arg(&dc)
24965 .arg(&ds)
24966 .arg(&nv)
24967 .arg(&nk)
24968 .arg(&kd)
24969 .arg(&hki);
24970 unsafe {
24971 b.launch(cfg)?;
24972 }
24973 }
24974 match pad_len {
24975 Some(len_d) => {
24976 let f = self.func("ssm_conv_ring_update_dev_f32");
24977 let n = conv_dim * (d_conv - 1);
24978 let cfg = LaunchConfig::for_num_elems(n as u32);
24979 let (cd, dc) = (conv_dim as i32, d_conv as i32);
24980 let __s_b = self.gpu.stream();
24981 let mut b = __s_b.launch_builder(&f);
24982 b.arg(qkv_tm).arg(conv_state).arg(len_d).arg(&cd).arg(&dc);
24983 unsafe {
24984 b.launch(cfg)?;
24985 }
24986 }
24987 None => {
24988 let f = self.func("ssm_conv_ring_update_f32");
24989 let n = conv_dim * (d_conv - 1);
24990 let cfg = LaunchConfig::for_num_elems(n as u32);
24991 let (cd, ti, dc) = (conv_dim as i32, t as i32, d_conv as i32);
24992 let __s_b = self.gpu.stream();
24993 let mut b = __s_b.launch_builder(&f);
24994 b.arg(qkv_tm).arg(conv_state).arg(&cd).arg(&ti).arg(&dc);
24995 unsafe {
24996 b.launch(cfg)?;
24997 }
24998 }
24999 }
25000 Ok(())
25001 }
25002
25003 pub fn gdn_chunk_alloc(
25007 &self,
25008 n_head: usize,
25009 t: usize,
25010 c: usize,
25011 hk: usize,
25012 ) -> Result<GdnChunkBufs, Box<dyn std::error::Error>> {
25013 const D: usize = 128;
25014 assert!(
25015 c == 32,
25016 "gdn_chunk_alloc: varlen chain is the C==32 mma pair"
25017 );
25018 let h = n_head;
25019 let nc = (t + c - 1) / c;
25020 Ok(GdnChunkBufs {
25021 gcum: self.uninit(t * h)?,
25022 a: self.uninit(nc * h * c * c)?,
25023 p: self.uninit(nc * h * c * c)?,
25024 u: self.uninit(nc * h * c * D)?,
25025 w: self.uninit(nc * h * c * D)?,
25026 kb16: self.alloc_u8_uninit(t * hk * D * 2)?,
25027 wb16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
25028 y16: self.alloc_u8_uninit(nc * h * c * D * 2)?,
25029 ssnap16: self.alloc_u8_uninit(nc * h * D * D * 2)?,
25030 qb16: self.alloc_u8_uninit(t * hk * D * 2)?,
25031 pb16: self.alloc_u8_uninit(nc * h * c * c * 2)?,
25032 o: self.uninit(D * h * t)?,
25033 t,
25034 nc,
25035 })
25036 }
25037
25038 pub fn f32_to_bf16_v(
25040 &self,
25041 x: &cudarc::driver::CudaView<f32>,
25042 dst: &mut CudaSlice<u8>,
25043 n: usize,
25044 ) -> Result<(), Box<dyn std::error::Error>> {
25045 let f = self.func("f32_to_bf16_bulk");
25046 let ni = n as i64;
25047 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
25048 let __s_b = self.gpu.stream();
25049 let mut b = __s_b.launch_builder(&f);
25050 b.arg(x).arg(dst).arg(&ni);
25051 unsafe {
25052 b.launch(cfg)?;
25053 }
25054 Ok(())
25055 }
25056
25057 pub fn f32_to_bf16_into(
25059 &self,
25060 x: &CudaSlice<f32>,
25061 dst: &mut CudaSlice<u8>,
25062 n: usize,
25063 ) -> Result<(), Box<dyn std::error::Error>> {
25064 let f = self.func("f32_to_bf16_bulk");
25065 let ni = n as i64;
25066 let cfg = LaunchConfig::for_num_elems((n as u32).div_ceil(4));
25067 let __s_b = self.gpu.stream();
25068 let mut b = __s_b.launch_builder(&f);
25069 b.arg(x).arg(dst).arg(&ni);
25070 unsafe {
25071 b.launch(cfg)?;
25072 }
25073 Ok(())
25074 }
25075
25076 pub fn gdn_chunk_k123_vl8(
25079 &self,
25080 seqs: &[GdnSeqVl],
25081 n_head: usize,
25082 hk: usize,
25083 wq: Option<&GdnWVl8>,
25084 ) -> Result<(), Box<dyn std::error::Error>> {
25085 let b = seqs.len();
25086 assert!(b >= 1 && b <= 8, "gdn_chunk_k123_vl8: 1..=8 sequences");
25087 let mut packed = [GdnSeqVl::default(); 8];
25088 packed[..b].copy_from_slice(seqs);
25089 let v = GdnVl8(packed);
25090 let (hi, ci) = (n_head as i32, 32i32);
25091 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
25092 {
25093 let f = self.func("gdn_chunk_cumgate_vl");
25094 let cfg = LaunchConfig {
25095 grid_dim: (max_nc, n_head as u32, b as u32),
25096 block_dim: (32, 1, 1),
25097 shared_mem_bytes: 0,
25098 };
25099 let __s_lb = self.gpu.stream();
25100 let mut lb = __s_lb.launch_builder(&f);
25101 lb.arg(&v).arg(&hi).arg(&ci);
25102 unsafe {
25103 lb.launch(cfg)?;
25104 }
25105 }
25106 let hki = hk as i32;
25107 if let Some(w) = wq {
25108 let f = self.func("gdn_k2_wgmma_vl");
25110 let cfg = LaunchConfig {
25111 grid_dim: (max_nc, n_head as u32, b as u32),
25112 block_dim: (128, 1, 1),
25113 shared_mem_bytes: 0,
25114 };
25115 let __s_lb = self.gpu.stream();
25116 let mut lb = __s_lb.launch_builder(&f);
25117 lb.arg(&v).arg(w).arg(&hi).arg(&ci).arg(&hki);
25118 unsafe {
25119 lb.launch(cfg)?;
25120 }
25121 } else {
25122 let f = self.func("gdn_chunk_attn_vl");
25123 f.set_attribute(
25124 CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES,
25125 GDN_K2_DYNAMIC_SHARED_BYTES as i32,
25126 )?;
25127 let cfg = LaunchConfig {
25128 grid_dim: (max_nc, n_head as u32, b as u32),
25129 block_dim: (256, 1, 1),
25130 shared_mem_bytes: GDN_K2_DYNAMIC_SHARED_BYTES,
25131 };
25132 let __s_lb = self.gpu.stream();
25133 let mut lb = __s_lb.launch_builder(&f);
25134 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
25135 unsafe {
25136 lb.launch(cfg)?;
25137 }
25138 }
25139 {
25140 let f = self.func("gdn_chunk_solve32_vl");
25141 let cfg = LaunchConfig {
25142 grid_dim: (max_nc, n_head as u32, b as u32),
25143 block_dim: (256, 1, 1),
25144 shared_mem_bytes: 0,
25145 };
25146 let __s_lb = self.gpu.stream();
25147 let mut lb = __s_lb.launch_builder(&f);
25148 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
25149 unsafe {
25150 lb.launch(cfg)?;
25151 }
25152 }
25153 Ok(())
25154 }
25155
25156 #[allow(clippy::too_many_arguments)]
25160 pub fn gdn_prep_vl8(
25161 &self,
25162 seqs: &[GdnPrepVl],
25163 conv_w: &CudaSlice<f32>,
25164 dt_bias: &CudaSlice<f32>,
25165 a: &CudaSlice<f32>,
25166 conv_dim: usize,
25167 d_conv: usize,
25168 d_state: usize,
25169 num_v: usize,
25170 num_k: usize,
25171 key_dim: usize,
25172 hk: usize,
25173 eps: f32,
25174 ) -> Result<(), Box<dyn std::error::Error>> {
25175 let b = seqs.len();
25176 assert!(b >= 1 && b <= 8);
25177 let mut packed = [GdnPrepVl::default(); 8];
25178 packed[..b].copy_from_slice(seqs);
25179 let v = GdnPrepVl8(packed);
25180 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
25181 let (cdi, dci) = (conv_dim as i32, d_conv as i32);
25182 let conv_fuse = std::env::var("MEMRA_CONV_FUSE").as_deref() != Ok("0");
25183 assert!(
25184 conv_fuse || hk == num_v,
25185 "de-broadcast requires the fused conv"
25186 );
25187 if conv_fuse {
25188 let f = self.func("ssm_conv1d_gdn_state_vl");
25189 let cfg = LaunchConfig {
25190 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
25191 block_dim: (256, 1, 1),
25192 shared_mem_bytes: 0,
25193 };
25194 let (dsi, nvi, nki, kdi, hki) = (
25195 d_state as i32,
25196 num_v as i32,
25197 num_k as i32,
25198 key_dim as i32,
25199 hk as i32,
25200 );
25201 let __s_lb = self.gpu.stream();
25202 let mut lb = __s_lb.launch_builder(&f);
25203 lb.arg(&v)
25204 .arg(conv_w)
25205 .arg(&cdi)
25206 .arg(&dci)
25207 .arg(&dsi)
25208 .arg(&nvi)
25209 .arg(&nki)
25210 .arg(&kdi)
25211 .arg(&hki);
25212 unsafe {
25213 lb.launch(cfg)?;
25214 }
25215 } else {
25216 let f = self.func("ssm_conv1d_tm_state_vl");
25217 let cfg = LaunchConfig {
25218 grid_dim: ((conv_dim as u32).div_ceil(256), max_t, b as u32),
25219 block_dim: (256, 1, 1),
25220 shared_mem_bytes: 0,
25221 };
25222 let __s_lb = self.gpu.stream();
25223 let mut lb = __s_lb.launch_builder(&f);
25224 lb.arg(&v).arg(conv_w).arg(&cdi).arg(&dci);
25225 unsafe {
25226 lb.launch(cfg)?;
25227 }
25228 }
25229 {
25230 let f = self.func("ssm_conv_ring_update_vl");
25231 let n = (conv_dim * (d_conv - 1)) as u32;
25232 let cfg = LaunchConfig {
25233 grid_dim: (n.div_ceil(256), 1, b as u32),
25234 block_dim: (256, 1, 1),
25235 shared_mem_bytes: 0,
25236 };
25237 let __s_lb = self.gpu.stream();
25238 let mut lb = __s_lb.launch_builder(&f);
25239 lb.arg(&v).arg(&cdi).arg(&dci);
25240 unsafe {
25241 lb.launch(cfg)?;
25242 }
25243 }
25244 if !conv_fuse {
25245 let f = self.func("qkv_to_gdn_repack_vl");
25246 let n = max_t * (num_v * d_state) as u32;
25247 let cfg = LaunchConfig {
25248 grid_dim: (n.div_ceil(256), 1, b as u32),
25249 block_dim: (256, 1, 1),
25250 shared_mem_bytes: 0,
25251 };
25252 let (dsi, nvi, nki, kdi) = (d_state as i32, num_v as i32, num_k as i32, key_dim as i32);
25253 let __s_lb = self.gpu.stream();
25254 let mut lb = __s_lb.launch_builder(&f);
25255 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&nki).arg(&kdi);
25256 unsafe {
25257 lb.launch(cfg)?;
25258 }
25259 }
25260 if Self::l2_v2_on(d_state) {
25261 let f = self.func("gdn_l2_v2_vl");
25262 let cfg = LaunchConfig {
25263 grid_dim: ((max_t * hk as u32).div_ceil(8), 2, b as u32),
25264 block_dim: (256, 1, 1),
25265 shared_mem_bytes: 0,
25266 };
25267 let (dsi, nvi) = (d_state as i32, hk as i32);
25268 let __s_lb = self.gpu.stream();
25269 let mut lb = __s_lb.launch_builder(&f);
25270 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
25271 unsafe {
25272 lb.launch(cfg)?;
25273 }
25274 } else {
25275 let f = self.func("gdn_l2_vl");
25276 let cfg = LaunchConfig {
25277 grid_dim: (max_t * hk as u32, 2, b as u32),
25278 block_dim: (256, 1, 1),
25279 shared_mem_bytes: 0,
25280 };
25281 let (dsi, nvi) = (d_state as i32, hk as i32);
25282 let __s_lb = self.gpu.stream();
25283 let mut lb = __s_lb.launch_builder(&f);
25284 lb.arg(&v).arg(&dsi).arg(&nvi).arg(&eps);
25285 unsafe {
25286 lb.launch(cfg)?;
25287 }
25288 }
25289 {
25290 let f = self.func("gdn_gate_prep_vl");
25291 let n = max_t * num_v as u32;
25292 let cfg = LaunchConfig {
25293 grid_dim: (n.div_ceil(256), 1, b as u32),
25294 block_dim: (256, 1, 1),
25295 shared_mem_bytes: 0,
25296 };
25297 let nvi = num_v as i32;
25298 let __s_lb = self.gpu.stream();
25299 let mut lb = __s_lb.launch_builder(&f);
25300 lb.arg(&v).arg(dt_bias).arg(a).arg(&nvi);
25301 unsafe {
25302 lb.launch(cfg)?;
25303 }
25304 }
25305 Ok(())
25306 }
25307
25308 pub fn gdn_mirror_vl8(
25310 &self,
25311 seqs: &[GdnSeqVl],
25312 n_head: usize,
25313 which: i32,
25314 hk: usize,
25315 ) -> Result<(), Box<dyn std::error::Error>> {
25316 let b = seqs.len();
25317 assert!(b >= 1 && b <= 8);
25318 let mut packed = [GdnSeqVl::default(); 8];
25319 packed[..b].copy_from_slice(seqs);
25320 let v = GdnVl8(packed);
25321 let ept = (if which == 0 { hk } else { n_head } * 128) as i32;
25322 let max_n = seqs
25323 .iter()
25324 .map(|s| {
25325 if which == 0 {
25326 s.t as i64 * ept as i64
25327 } else {
25328 s.nc as i64 * ept as i64 * 32
25329 }
25330 })
25331 .max()
25332 .unwrap();
25333 let f = self.func("gdn_mirror_vl");
25334 let blocks = ((max_n as u32).div_ceil(4)).div_ceil(256);
25335 let cfg = LaunchConfig {
25336 grid_dim: (blocks, 1, b as u32),
25337 block_dim: (256, 1, 1),
25338 shared_mem_bytes: 0,
25339 };
25340 let __s_lb = self.gpu.stream();
25341 let mut lb = __s_lb.launch_builder(&f);
25342 lb.arg(&v).arg(&ept).arg(&which);
25343 unsafe {
25344 lb.launch(cfg)?;
25345 }
25346 Ok(())
25347 }
25348
25349 pub fn gdn_tail_vl8(
25351 &self,
25352 seqs: &[GdnPrepVl],
25353 norm_w: &CudaSlice<f32>,
25354 d_state: usize,
25355 num_v: usize,
25356 eps: f32,
25357 ) -> Result<(), Box<dyn std::error::Error>> {
25358 let b = seqs.len();
25359 assert!(b >= 1 && b <= 8);
25360 let mut packed = [GdnPrepVl::default(); 8];
25361 packed[..b].copy_from_slice(seqs);
25362 let v = GdnPrepVl8(packed);
25363 let max_t = seqs.iter().map(|s| s.t).max().unwrap() as u32;
25364 let f = self.func("gated_rmsnorm_f16out_vl");
25365 let cfg = LaunchConfig {
25367 grid_dim: (max_t * num_v as u32, 1, b as u32),
25368 block_dim: (128, 1, 1),
25369 shared_mem_bytes: 0,
25370 };
25371 let (dsi, nvi) = (d_state as i32, num_v as i32);
25372 let __s_lb = self.gpu.stream();
25373 let mut lb = __s_lb.launch_builder(&f);
25374 lb.arg(&v).arg(norm_w).arg(&dsi).arg(&nvi).arg(&eps);
25375 unsafe {
25376 lb.launch(cfg)?;
25377 }
25378 Ok(())
25379 }
25380
25381 pub fn addr_f32(&self, x: &CudaSlice<f32>) -> u64 {
25384 use cudarc::driver::DevicePtr;
25385 let s = self.gpu.stream();
25386 let (p, _g) = x.device_ptr(&s);
25387 p as u64
25388 }
25389 pub fn addr_f32_mut(&self, x: &mut CudaSlice<f32>) -> u64 {
25390 use cudarc::driver::DevicePtrMut;
25391 let s = self.gpu.stream();
25392 let (p, _g) = x.device_ptr_mut(&s);
25393 p as u64
25394 }
25395 pub fn addr_f32v(&self, x: &cudarc::driver::CudaView<f32>) -> u64 {
25396 use cudarc::driver::DevicePtr;
25397 let s = self.gpu.stream();
25398 let (p, _g) = x.device_ptr(&s);
25399 p as u64
25400 }
25401 pub fn addr_u8(&self, x: &CudaSlice<u8>) -> u64 {
25402 use cudarc::driver::DevicePtr;
25403 let s = self.gpu.stream();
25404 let (p, _g) = x.device_ptr(&s);
25405 p as u64
25406 }
25407
25408 pub fn gdn_chunk_vl8(
25412 &self,
25413 seqs: &[GdnSeqVl],
25414 n_head: usize,
25415 scale: f32,
25416 hk: usize,
25417 wq: Option<&GdnWVl8>,
25418 ) -> Result<(), Box<dyn std::error::Error>> {
25419 const NSPLIT: u32 = 4;
25420 let b = seqs.len();
25421 assert!(b >= 1 && b <= 8, "gdn_chunk_vl8: 1..=8 sequences");
25422 let mut packed = [GdnSeqVl::default(); 8];
25423 packed[..b].copy_from_slice(seqs);
25424 let v = GdnVl8(packed);
25425 let (hi, ci) = (n_head as i32, 32i32);
25426 let max_nc = seqs.iter().map(|a| a.nc).max().unwrap() as u32;
25427 let hki = hk as i32;
25428 if let Some(w) = wq {
25429 let f = self.func("gdn_k45_wgmma_vl");
25431 let cfg = LaunchConfig {
25432 grid_dim: (n_head as u32, NSPLIT, b as u32),
25433 block_dim: (256, 1, 1),
25434 shared_mem_bytes: 0,
25435 };
25436 let __s_lb = self.gpu.stream();
25437 let mut lb = __s_lb.launch_builder(&f);
25438 lb.arg(&v).arg(w).arg(&scale).arg(&hi).arg(&ci).arg(&hki);
25439 unsafe {
25440 lb.launch(cfg)?;
25441 }
25442 let _ = max_nc;
25443 return Ok(());
25444 }
25445 {
25446 let f = self.func("gdn_chunk_state_mma_vl");
25447 let cfg = LaunchConfig {
25448 grid_dim: (n_head as u32, NSPLIT, b as u32),
25449 block_dim: (256, 1, 1),
25450 shared_mem_bytes: 0,
25451 };
25452 let __s_lb = self.gpu.stream();
25453 let mut lb = __s_lb.launch_builder(&f);
25454 lb.arg(&v).arg(&hi).arg(&ci).arg(&hki);
25455 unsafe {
25456 lb.launch(cfg)?;
25457 }
25458 }
25459 {
25460 let f = self.func("gdn_chunk_output_mma_vl");
25461 let cfg = LaunchConfig {
25462 grid_dim: (max_nc, n_head as u32, b as u32),
25463 block_dim: (256, 1, 1),
25464 shared_mem_bytes: 0,
25465 };
25466 let __s_lb = self.gpu.stream();
25467 let mut lb = __s_lb.launch_builder(&f);
25468 lb.arg(&v).arg(&hi).arg(&ci).arg(&scale).arg(&hki);
25469 unsafe {
25470 lb.launch(cfg)?;
25471 }
25472 }
25473 Ok(())
25474 }
25475 pub fn gdn_scan_chunked(
25476 &self,
25477 q: &CudaSlice<f32>,
25478 k: &CudaSlice<f32>,
25479 v: &CudaSlice<f32>,
25480 g: &CudaSlice<f32>,
25481 beta: &CudaSlice<f32>,
25482 kb16_pre: Option<&CudaSlice<u8>>,
25483 qb16_pre: Option<&CudaSlice<u8>>,
25484 state_in: &CudaSlice<f32>,
25485 state_out: &mut CudaSlice<f32>,
25486 o: &mut CudaSlice<f32>,
25487 n_head: usize,
25488 t: usize,
25489 scale: f32,
25490 c: usize,
25491 hk: usize,
25492 ) -> Result<(), Box<dyn std::error::Error>> {
25493 const D: usize = 128;
25494 const NSPLIT: u32 = 4;
25495 assert!(c >= 1 && c <= 128, "gdn_scan_chunked: C must be in 1..=128");
25496 let h = n_head;
25497 let nc = (t + c - 1) / c;
25498 let (hi, ti, ci) = (h as i32, t as i32, c as i32);
25499 let gdn_mma_pre = !portable_mma_gated()
25504 && c == 32
25505 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
25506 Ok("1") => true,
25507 Ok("0") => false,
25508 _ => gdn_mma_default_on(),
25509 };
25510 let mut wb16_pre: Option<CudaSlice<u8>> = if gdn_mma_pre {
25511 Some(self.alloc_u8_uninit(nc * h * c * D * 2)?)
25512 } else {
25513 None
25514 };
25515 let gdn_wgmma_pre = cfg!(memra_hopper_mma)
25520 && gdn_mma_pre
25521 && std::env::var("MEMRA_GDN_WGMMA").as_deref() != Ok("0");
25522 let nk = t * hk * D;
25523 let mut kb16_local: Option<CudaSlice<u8>> = None;
25524 if gdn_mma_pre && kb16_pre.is_none() {
25525 let mut kb = self.alloc_u8_uninit(nk * 2)?;
25526 let f = self.func("f32_to_bf16_bulk");
25527 let n2 = nk as i64;
25528 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
25529 let __s_b = self.gpu.stream();
25530 let mut b = __s_b.launch_builder(&f);
25531 b.arg(k).arg(&mut kb).arg(&n2);
25532 unsafe {
25533 b.launch(cfg2)?;
25534 }
25535 kb16_local = Some(kb);
25536 }
25537 let kb16_ref0: Option<&CudaSlice<u8>> = kb16_local.as_ref().or(kb16_pre);
25538 if let Some(kb) = kb16_pre {
25539 assert!(kb.len() >= nk * 2, "kb16_pre too small");
25540 }
25541 let mut qb16: Option<CudaSlice<u8>> = None;
25542 let mut pb16: Option<CudaSlice<u8>> = None;
25543 if gdn_wgmma_pre {
25544 if qb16_pre.is_none() {
25547 let mut qb = self.alloc_u8_uninit(nk * 2)?;
25548 let f = self.func("f32_to_bf16_bulk");
25549 let n2 = nk as i64;
25550 let cfg2 = LaunchConfig::for_num_elems((nk as u32).div_ceil(4));
25551 let __s_b = self.gpu.stream();
25552 let mut b = __s_b.launch_builder(&f);
25553 b.arg(q).arg(&mut qb).arg(&n2);
25554 unsafe {
25555 b.launch(cfg2)?;
25556 }
25557 qb16 = Some(qb);
25558 } else if let Some(qb) = qb16_pre {
25559 assert!(qb.len() >= nk * 2, "qb16_pre too small");
25560 }
25561 pb16 = Some(self.alloc_u8_uninit(nc * h * c * c * 2)?);
25562 }
25563 let qb16_ref0: Option<&CudaSlice<u8>> = qb16.as_ref().or(qb16_pre);
25564 let k2w = if gdn_wgmma_pre {
25565 Some((
25566 *qb16_ref0.as_ref().unwrap(),
25567 *kb16_ref0.as_ref().unwrap(),
25568 pb16.as_mut().unwrap(),
25569 ))
25570 } else {
25571 None
25572 };
25573 let (gcum, p, u, w) =
25574 self.gdn_chunk_k123(q, k, v, g, beta, wb16_pre.as_mut(), n_head, t, c, hk, k2w)?;
25575 let _ = &w;
25576 let mut y = self.uninit(nc * h * c * D)?;
25577 let mut ssnap = self.uninit(nc * h * D * D)?; let gdn_mma = !portable_mma_gated()
25591 && c == 32
25592 && match std::env::var("MEMRA_GDN_MMA").as_deref() {
25593 Ok("1") => true,
25594 Ok("0") => false,
25595 _ => gdn_mma_default_on(),
25596 };
25597 if gdn_mma {
25598 let wb16 = wb16_pre
25599 .take()
25600 .expect("mma path pre-allocates wb16 (K3 store fold)");
25601 let kb16_ref: &CudaSlice<u8> = kb16_ref0.expect("mma path pre-builds kb16 above K123");
25602 if gdn_wgmma_pre {
25614 let qb16 = qb16_ref0.unwrap();
25616 let pb16 = pb16.as_ref().unwrap();
25617 {
25618 let f = self.func("gdn_k45_wgmma");
25619 let cfg = LaunchConfig {
25620 grid_dim: (h as u32, 4, 1),
25621 block_dim: (256, 1, 1),
25622 shared_mem_bytes: 0,
25623 };
25624 let hki = hk as i32;
25625 let __s_b = self.gpu.stream();
25626 let mut b = __s_b.launch_builder(&f);
25627 b.arg(kb16_ref)
25628 .arg(&gcum)
25629 .arg(beta)
25630 .arg(&u)
25631 .arg(&wb16)
25632 .arg(qb16)
25633 .arg(pb16)
25634 .arg(o)
25635 .arg(&scale)
25636 .arg(state_in)
25637 .arg(&mut *state_out)
25638 .arg(&hi)
25639 .arg(&ti)
25640 .arg(&ci)
25641 .arg(&hki);
25642 unsafe {
25643 b.launch(cfg)?;
25644 }
25645 }
25646 return Ok(());
25647 }
25648 let mut y16 = self.alloc_u8_uninit(nc * h * c * D * 2)?;
25652 let mut ssnap16 = self.alloc_u8_uninit(nc * h * D * D * 2)?;
25653 {
25654 let f = self.func("gdn_chunk_state_mma");
25655 let cfg = LaunchConfig {
25656 grid_dim: (h as u32, NSPLIT, 1),
25657 block_dim: (256, 1, 1),
25658 shared_mem_bytes: 0,
25659 };
25660 let hki = hk as i32;
25661 let __s_b = self.gpu.stream();
25662 let mut b = __s_b.launch_builder(&f);
25663 b.arg(kb16_ref)
25664 .arg(&gcum)
25665 .arg(beta)
25666 .arg(&u)
25667 .arg(&wb16)
25668 .arg(&mut y16)
25669 .arg(&mut ssnap16)
25670 .arg(state_in)
25671 .arg(&mut *state_out)
25672 .arg(&hi)
25673 .arg(&ti)
25674 .arg(&ci)
25675 .arg(&hki);
25676 unsafe {
25677 b.launch(cfg)?;
25678 }
25679 }
25680 {
25681 let f = self.func("gdn_chunk_output_mma");
25683 let jt = ((c + 31) / 32) as u32;
25684 let cfg = LaunchConfig {
25685 grid_dim: (nc as u32, h as u32, jt),
25686 block_dim: (256, 1, 1),
25687 shared_mem_bytes: 0,
25688 };
25689 let hki = hk as i32;
25690 let __s_b = self.gpu.stream();
25691 let mut b = __s_b.launch_builder(&f);
25692 b.arg(q)
25693 .arg(&gcum)
25694 .arg(&p)
25695 .arg(&y16)
25696 .arg(&ssnap16)
25697 .arg(o)
25698 .arg(&hi)
25699 .arg(&ti)
25700 .arg(&ci)
25701 .arg(&scale)
25702 .arg(&hki);
25703 unsafe {
25704 b.launch(cfg)?;
25705 }
25706 }
25707 return Ok(());
25708 }
25709 {
25710 let f = self.func("gdn_chunk_state_f32");
25712 let cfg = LaunchConfig {
25713 grid_dim: (h as u32, NSPLIT, 1),
25714 block_dim: (256, 1, 1),
25715 shared_mem_bytes: 0,
25716 };
25717 let __s_b = self.gpu.stream();
25718 let mut b = __s_b.launch_builder(&f);
25719 b.arg(k)
25720 .arg(&gcum)
25721 .arg(beta)
25722 .arg(&u)
25723 .arg(&w)
25724 .arg(&mut y)
25725 .arg(&mut ssnap)
25726 .arg(state_in)
25727 .arg(&mut *state_out)
25728 .arg(&hi)
25729 .arg(&ti)
25730 .arg(&ci);
25731 unsafe {
25732 b.launch(cfg)?;
25733 }
25734 }
25735 {
25736 let f = self.func("gdn_chunk_output_f32");
25738 let jt = ((c + 31) / 32) as u32;
25739 let cfg = LaunchConfig {
25740 grid_dim: (nc as u32, h as u32, jt),
25741 block_dim: (256, 1, 1),
25742 shared_mem_bytes: 0,
25743 };
25744 let __s_b = self.gpu.stream();
25745 let mut b = __s_b.launch_builder(&f);
25746 b.arg(q)
25747 .arg(&gcum)
25748 .arg(&p)
25749 .arg(&y)
25750 .arg(&ssnap)
25751 .arg(o)
25752 .arg(&hi)
25753 .arg(&ti)
25754 .arg(&ci)
25755 .arg(&scale);
25756 unsafe {
25757 b.launch(cfg)?;
25758 }
25759 }
25760 Ok(())
25761 }
25762
25763 #[allow(clippy::too_many_arguments)]
25772 #[allow(clippy::too_many_arguments)]
25773 pub fn gdn_scan_prefill(
25774 &self,
25775 q: &CudaSlice<f32>,
25776 k: &CudaSlice<f32>,
25777 v: &CudaSlice<f32>,
25778 g: &CudaSlice<f32>,
25779 beta: &CudaSlice<f32>,
25780 kb16_pre: Option<&CudaSlice<u8>>,
25781 qb16_pre: Option<&CudaSlice<u8>>,
25782 state_in: &CudaSlice<f32>,
25783 state_out: &mut CudaSlice<f32>,
25784 o: &mut CudaSlice<f32>,
25785 n_head: usize,
25786 t: usize,
25787 scale: f32,
25788 hk: usize,
25789 ) -> Result<(), Box<dyn std::error::Error>> {
25790 if std::env::var("MEMRA_GDN_DIFF").is_ok() && t >= 16 {
25791 assert!(hk == n_head, "GDN_DIFF oracle is broadcast-only");
25792 return self.gdn_scan_diff(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale);
25793 }
25794 if Self::gdn_chunked_enabled() && t >= 16 {
25795 self.gdn_scan_chunked(
25796 q,
25797 k,
25798 v,
25799 g,
25800 beta,
25801 kb16_pre,
25802 qb16_pre,
25803 state_in,
25804 state_out,
25805 o,
25806 n_head,
25807 t,
25808 scale,
25809 Self::gdn_chunk_size(),
25810 hk,
25811 )
25812 } else {
25813 assert!(
25814 hk == n_head,
25815 "s128 scan is broadcast-only (prep guarantees by predicate)"
25816 );
25817 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)
25818 }
25819 }
25820
25821 #[allow(clippy::too_many_arguments)]
25823 fn gdn_scan_diff(
25824 &self,
25825 q: &CudaSlice<f32>,
25826 k: &CudaSlice<f32>,
25827 v: &CudaSlice<f32>,
25828 g: &CudaSlice<f32>,
25829 beta: &CudaSlice<f32>,
25830 state_in: &CudaSlice<f32>,
25831 state_out: &mut CudaSlice<f32>,
25832 o: &mut CudaSlice<f32>,
25833 n_head: usize,
25834 t: usize,
25835 scale: f32,
25836 ) -> Result<(), Box<dyn std::error::Error>> {
25837 static CALL: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
25838 let call = CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
25839 let mut o_c = self.uninit(o.len())?;
25840 let mut st_c = self.uninit(state_out.len())?;
25841 self.gdn_scan_chunked(
25842 q,
25843 k,
25844 v,
25845 g,
25846 beta,
25847 None,
25848 None,
25849 state_in,
25850 &mut st_c,
25851 &mut o_c,
25852 n_head,
25853 t,
25854 scale,
25855 Self::gdn_chunk_size(),
25856 n_head,
25857 )?;
25858 self.gdn_scan_s128(q, k, v, g, beta, state_in, state_out, o, n_head, t, scale)?;
25859 let (oh_s, oh_c) = (self.dtoh(o)?, self.dtoh(&o_c)?);
25860 let (sh_s, sh_c) = (self.dtoh(state_out)?, self.dtoh(&st_c)?);
25861 let stats = |a: &[f32], b: &[f32]| -> (f32, f32, f64) {
25862 let mut max_abs = 0f32;
25863 let mut max_rel = 0f32;
25864 let mut sum_rel = 0f64;
25865 for (x, y) in a.iter().zip(b) {
25866 let ad = (x - y).abs();
25867 let rel = ad / x.abs().max(y.abs()).max(1e-3);
25868 if ad > max_abs {
25869 max_abs = ad;
25870 }
25871 if rel > max_rel {
25872 max_rel = rel;
25873 }
25874 sum_rel += rel as f64;
25875 }
25876 (max_abs, max_rel, sum_rel / a.len() as f64)
25877 };
25878 let (o_ma, o_mr, o_mean) = stats(&oh_s, &oh_c);
25879 let (s_ma, s_mr, s_mean) = stats(&sh_s, &sh_c);
25880 println!(
25881 "[gdn-diff call {call:3} T={t} C={}] out: max_abs={o_ma:.3e} max_rel={o_mr:.3e} mean_rel={o_mean:.3e} | \
25882 state: max_abs={s_ma:.3e} max_rel={s_mr:.3e} mean_rel={s_mean:.3e}",
25883 Self::gdn_chunk_size()
25884 );
25885 Ok(())
25886 }
25887
25888 pub fn gdn_glog(
25890 &self,
25891 alpha: &CudaSlice<f32>,
25892 dt_bias: &CudaSlice<f32>,
25893 a: &CudaSlice<f32>,
25894 g_log: &mut CudaSlice<f32>,
25895 n_head: usize,
25896 t: usize,
25897 ) -> Result<(), Box<dyn std::error::Error>> {
25898 let f = self.func("gdn_glog_f32");
25899 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
25900 let (h, ti) = (n_head as i32, t as i32);
25901 let __s_b = self.gpu.stream();
25902 let mut b = __s_b.launch_builder(&f);
25903 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
25904 unsafe {
25905 b.launch(cfg)?;
25906 }
25907 Ok(())
25908 }
25909
25910 pub fn sigmoid_v(
25913 &self,
25914 x: &cudarc::driver::CudaView<f32>,
25915 y: &mut CudaSlice<f32>,
25916 n: usize,
25917 ) -> Result<(), Box<dyn std::error::Error>> {
25918 let f = self.func("sigmoid_f32");
25919 let cfg = LaunchConfig::for_num_elems(n as u32);
25920 let ni = n as i32;
25921 let __s_b = self.gpu.stream();
25922 let mut b = __s_b.launch_builder(&f);
25923 b.arg(x).arg(y).arg(&ni);
25924 unsafe {
25925 b.launch(cfg)?;
25926 }
25927 Ok(())
25928 }
25929
25930 pub fn gdn_glog_v(
25931 &self,
25932 alpha: &cudarc::driver::CudaView<f32>,
25933 dt_bias: &CudaSlice<f32>,
25934 a: &CudaSlice<f32>,
25935 g_log: &mut CudaSlice<f32>,
25936 n_head: usize,
25937 t: usize,
25938 ) -> Result<(), Box<dyn std::error::Error>> {
25939 let f = self.func("gdn_glog_f32");
25940 let cfg = LaunchConfig::for_num_elems((n_head * t) as u32);
25941 let (h, ti) = (n_head as i32, t as i32);
25942 let __s_b = self.gpu.stream();
25943 let mut b = __s_b.launch_builder(&f);
25944 b.arg(alpha).arg(dt_bias).arg(a).arg(g_log).arg(&h).arg(&ti);
25945 unsafe {
25946 b.launch(cfg)?;
25947 }
25948 Ok(())
25949 }
25950
25951 pub fn sigmoid(
25952 &self,
25953 x: &CudaSlice<f32>,
25954 y: &mut CudaSlice<f32>,
25955 n: usize,
25956 ) -> Result<(), Box<dyn std::error::Error>> {
25957 let f = self.func("sigmoid_f32");
25958 let cfg = LaunchConfig::for_num_elems(n as u32);
25959 let ni = n as i32;
25960 let __s_b = self.gpu.stream();
25961 let mut b = __s_b.launch_builder(&f);
25962 b.arg(x).arg(y).arg(&ni);
25963 unsafe {
25964 b.launch(cfg)?;
25965 }
25966 Ok(())
25967 }
25968
25969 pub fn sig_mul_f16out(
25972 &self,
25973 a: &CudaSlice<f32>,
25974 g: &CudaSlice<f32>,
25975 dst: &mut CudaSlice<f32>,
25976 dst16: &mut CudaSlice<u8>,
25977 n: usize,
25978 ) -> Result<(), Box<dyn std::error::Error>> {
25979 let f = self.func("sig_mul_f16out_f32");
25980 let cfg = LaunchConfig::for_num_elems(n as u32);
25981 let ni = n as i32;
25982 let __s_b = self.gpu.stream();
25983 let mut b = __s_b.launch_builder(&f);
25984 b.arg(a).arg(g).arg(dst).arg(dst16).arg(&ni);
25985 unsafe {
25986 b.launch(cfg)?;
25987 }
25988 Ok(())
25989 }
25990
25991 #[allow(clippy::too_many_arguments)]
26000 pub fn attn_head_gate(
26001 &self,
26002 a: &CudaSlice<f32>,
26003 g: &CudaSlice<f32>,
26004 dst: &mut CudaSlice<f32>,
26005 dst16: Option<&mut CudaSlice<u8>>,
26006 head_dim: usize,
26007 n_head: usize,
26008 t: usize,
26009 ) -> Result<(), Box<dyn std::error::Error>> {
26010 let f = self.func("attn_head_gate_f32");
26011 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
26012 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
26013 let d16: u64 = match dst16 {
26015 Some(d) => self.addr_u8(d),
26016 None => 0,
26017 };
26018 let __s_b = self.gpu.stream();
26019 let mut b = __s_b.launch_builder(&f);
26020 b.arg(a)
26021 .arg(g)
26022 .arg(dst)
26023 .arg(&d16)
26024 .arg(&hd)
26025 .arg(&nh)
26026 .arg(&ti);
26027 unsafe {
26028 b.launch(cfg)?;
26029 }
26030 Ok(())
26031 }
26032
26033 #[allow(clippy::too_many_arguments)]
26042 pub fn swiglu_clamped_mul_scaled(
26043 &self,
26044 gate: &CudaSlice<f32>,
26045 up: &CudaSlice<f32>,
26046 gs: f32,
26047 us: f32,
26048 limit: f32,
26049 dst: &mut CudaSlice<f32>,
26050 n: usize,
26051 ) -> Result<(), Box<dyn std::error::Error>> {
26052 debug_assert!(
26053 limit > 1e-6,
26054 "swiglu_clamped needs a live limit; use silu_mul_scaled"
26055 );
26056 let f = self.func("swiglu_clamped_mul_scaled_f32");
26057 let cfg = LaunchConfig::for_num_elems(n as u32);
26058 let ni = n as i32;
26059 let __s_b = self.gpu.stream();
26060 let mut b = __s_b.launch_builder(&f);
26061 b.arg(gate)
26062 .arg(up)
26063 .arg(&gs)
26064 .arg(&us)
26065 .arg(&limit)
26066 .arg(dst)
26067 .arg(&ni);
26068 unsafe {
26069 b.launch(cfg)?;
26070 }
26071 Ok(())
26072 }
26073
26074 pub fn gated_rmsnorm(
26076 &self,
26077 o: &CudaSlice<f32>,
26078 w: &CudaSlice<f32>,
26079 z: &CudaSlice<f32>,
26080 dst: &mut CudaSlice<f32>,
26081 ncols: usize,
26082 nrows: usize,
26083 eps: f32,
26084 ) -> Result<(), Box<dyn std::error::Error>> {
26085 let f = self.func("gated_rmsnorm_f32");
26086 let cfg = LaunchConfig {
26087 grid_dim: (nrows as u32, 1, 1),
26088 block_dim: (128, 1, 1),
26089 shared_mem_bytes: 0,
26090 };
26091 let (nc, e) = (ncols as i32, eps);
26092 let __s_b = self.gpu.stream();
26093 let mut b = __s_b.launch_builder(&f);
26094 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
26095 unsafe {
26096 b.launch(cfg)?;
26097 }
26098 Ok(())
26099 }
26100
26101 pub fn gated_rmsnorm_f16out(
26104 &self,
26105 o: &CudaSlice<f32>,
26106 w: &CudaSlice<f32>,
26107 z: &CudaSlice<f32>,
26108 dst: &mut CudaSlice<f32>,
26109 dst16: &mut CudaSlice<u8>,
26110 ncols: usize,
26111 nrows: usize,
26112 eps: f32,
26113 ) -> Result<(), Box<dyn std::error::Error>> {
26114 let f = self.func("gated_rmsnorm_f16out_f32");
26115 let cfg = LaunchConfig {
26117 grid_dim: (nrows as u32, 1, 1),
26118 block_dim: (128, 1, 1),
26119 shared_mem_bytes: 0,
26120 };
26121 let (nc, e) = (ncols as i32, eps);
26122 let __s_b = self.gpu.stream();
26123 let mut b = __s_b.launch_builder(&f);
26124 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
26125 unsafe {
26126 b.launch(cfg)?;
26127 }
26128 Ok(())
26129 }
26130
26131 #[allow(clippy::too_many_arguments)]
26135 pub fn add_rms_norm_zq8(
26136 &self,
26137 a: &CudaSlice<f32>,
26138 b_in: &CudaSlice<f32>,
26139 w: &CudaSlice<f32>,
26140 res: &mut CudaSlice<f32>,
26141 z: &mut CudaSlice<f32>,
26142 ncols: usize,
26143 nrows: usize,
26144 eps: f32,
26145 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
26146 assert!(ncols % 32 == 0);
26147 let mut q = self.alloc_uninit::<i8>(nrows * ncols)?;
26148 let mut d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
26149 let f = self.func("add_rms_norm_zq8");
26150 let cfg = LaunchConfig {
26151 grid_dim: (nrows as u32, 1, 1),
26152 block_dim: (1024, 1, 1),
26153 shared_mem_bytes: 0,
26154 };
26155 let (nc, ep) = (ncols as i32, eps);
26156 let __s_b = self.gpu.stream();
26157 let mut b = __s_b.launch_builder(&f);
26158 b.arg(a)
26159 .arg(b_in)
26160 .arg(w)
26161 .arg(res)
26162 .arg(z)
26163 .arg(&mut q)
26164 .arg(&mut d)
26165 .arg(&nc)
26166 .arg(&ep);
26167 unsafe {
26168 b.launch(cfg)?;
26169 }
26170 Ok((q, d))
26171 }
26172
26173 pub fn gated_rmsnorm_zv(
26178 &self,
26179 o: &CudaSlice<f32>,
26180 w: &CudaSlice<f32>,
26181 z: &cudarc::driver::CudaView<f32>,
26182 dst: &mut CudaSlice<f32>,
26183 ncols: usize,
26184 nrows: usize,
26185 eps: f32,
26186 ) -> Result<(), Box<dyn std::error::Error>> {
26187 let f = self.func("gated_rmsnorm_f32");
26188 let cfg = LaunchConfig {
26189 grid_dim: (nrows as u32, 1, 1),
26190 block_dim: (128, 1, 1),
26191 shared_mem_bytes: 0,
26192 };
26193 let (nc, e) = (ncols as i32, eps);
26194 let __s_b = self.gpu.stream();
26195 let mut b = __s_b.launch_builder(&f);
26196 b.arg(o).arg(w).arg(z).arg(dst).arg(&nc).arg(&e);
26197 unsafe {
26198 b.launch(cfg)?;
26199 }
26200 Ok(())
26201 }
26202
26203 pub fn gated_rmsnorm_f16out_zv(
26204 &self,
26205 o: &CudaSlice<f32>,
26206 w: &CudaSlice<f32>,
26207 z: &cudarc::driver::CudaView<f32>,
26208 dst: &mut CudaSlice<f32>,
26209 dst16: &mut CudaSlice<u8>,
26210 ncols: usize,
26211 nrows: usize,
26212 eps: f32,
26213 ) -> Result<(), Box<dyn std::error::Error>> {
26214 let f = self.func("gated_rmsnorm_f16out_f32");
26215 let cfg = LaunchConfig {
26217 grid_dim: (nrows as u32, 1, 1),
26218 block_dim: (128, 1, 1),
26219 shared_mem_bytes: 0,
26220 };
26221 let (nc, e) = (ncols as i32, eps);
26222 let __s_b = self.gpu.stream();
26223 let mut b = __s_b.launch_builder(&f);
26224 b.arg(o).arg(w).arg(z).arg(dst).arg(dst16).arg(&nc).arg(&e);
26225 unsafe {
26226 b.launch(cfg)?;
26227 }
26228 Ok(())
26229 }
26230
26231 pub fn gated_rmsnorm_q8_1(
26232 &self,
26233 o: &CudaSlice<f32>,
26234 w: &CudaSlice<f32>,
26235 z: &CudaSlice<f32>,
26236 ncols: usize,
26237 nrows: usize,
26238 eps: f32,
26239 ) -> Result<(CudaSlice<i8>, CudaSlice<f32>), Box<dyn std::error::Error>> {
26240 assert!(ncols % 32 == 0);
26241 let f = self.func("gated_rmsnorm_q8_1");
26242 let mut out_q = self.alloc_uninit::<i8>(nrows * ncols)?;
26243 let mut out_d = self.alloc_uninit::<f32>(nrows * (ncols / 32))?;
26244 let cfg = LaunchConfig {
26245 grid_dim: (nrows as u32, 1, 1),
26246 block_dim: (128, 1, 1),
26247 shared_mem_bytes: 0,
26248 };
26249 let (nc, ep) = (ncols as i32, eps);
26250 let __s_b = self.gpu.stream();
26251 let mut b = __s_b.launch_builder(&f);
26252 b.arg(o)
26253 .arg(w)
26254 .arg(z)
26255 .arg(&mut out_q)
26256 .arg(&mut out_d)
26257 .arg(&nc)
26258 .arg(&ep);
26259 unsafe {
26260 b.launch(cfg)?;
26261 }
26262 Ok((out_q, out_d))
26263 }
26264
26265 pub fn transpose(
26267 &self,
26268 inp: &CudaSlice<f32>,
26269 rows: usize,
26270 cols: usize,
26271 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
26272 let f = self.func("transpose_f32");
26273 let mut out = self.zeros(rows * cols)?;
26274 let cfg = LaunchConfig::for_num_elems((rows * cols) as u32);
26275 let (r, c) = (rows as i32, cols as i32);
26276 let __s_b = self.gpu.stream();
26277 let mut b = __s_b.launch_builder(&f);
26278 b.arg(inp).arg(&mut out).arg(&r).arg(&c);
26279 unsafe {
26280 b.launch(cfg)?;
26281 }
26282 Ok(out)
26283 }
26284
26285 pub fn repeat_heads(
26287 &self,
26288 inp: &CudaSlice<f32>,
26289 out: &mut CudaSlice<f32>,
26290 head_dim: usize,
26291 n_in: usize,
26292 n_out: usize,
26293 t: usize,
26294 ) -> Result<(), Box<dyn std::error::Error>> {
26295 let f = self.func("repeat_heads_f32");
26296 let cfg = LaunchConfig::for_num_elems((head_dim * n_out * t) as u32);
26297 let (hd, ni, no, ti) = (head_dim as i32, n_in as i32, n_out as i32, t as i32);
26298 let __s_b = self.gpu.stream();
26299 let mut b = __s_b.launch_builder(&f);
26300 b.arg(inp).arg(out).arg(&hd).arg(&ni).arg(&no).arg(&ti);
26301 unsafe {
26302 b.launch(cfg)?;
26303 }
26304 Ok(())
26305 }
26306
26307 pub fn q_gate_split(
26314 &self,
26315 qf: &CudaSlice<f32>,
26316 q_out: &mut CudaSlice<f32>,
26317 gate_out: &mut CudaSlice<f32>,
26318 head_dim: usize,
26319 n_head: usize,
26320 t: usize,
26321 ) -> Result<(), Box<dyn std::error::Error>> {
26322 memra_gguf::config::check_fused_q_gate_extent(qf.len(), head_dim, n_head, t)?;
26323 let out_need = head_dim * n_head * t;
26324 if q_out.len() < out_need || gate_out.len() < out_need {
26325 return Err(format!(
26326 "q_gate_split destinations too small: need {out_need} each, have q={} gate={}",
26327 q_out.len(),
26328 gate_out.len()
26329 )
26330 .into());
26331 }
26332 let f = self.func("q_gate_split_f32");
26333 let cfg = LaunchConfig::for_num_elems((head_dim * n_head * t) as u32);
26334 let (hd, nh, ti) = (head_dim as i32, n_head as i32, t as i32);
26335 let __s_b = self.gpu.stream();
26336 let mut b = __s_b.launch_builder(&f);
26337 b.arg(qf)
26338 .arg(q_out)
26339 .arg(gate_out)
26340 .arg(&hd)
26341 .arg(&nh)
26342 .arg(&ti);
26343 unsafe {
26344 b.launch(cfg)?;
26345 }
26346 Ok(())
26347 }
26348
26349 pub fn qkv_to_gdn_repack(
26353 &self,
26354 conv_out: &CudaSlice<f32>,
26355 q_g: &mut CudaSlice<f32>,
26356 k_g: &mut CudaSlice<f32>,
26357 v_g: &mut CudaSlice<f32>,
26358 d_state: usize,
26359 num_v: usize,
26360 num_k: usize,
26361 key_dim: usize,
26362 t: usize,
26363 ) -> Result<(), Box<dyn std::error::Error>> {
26364 let f = self.func("qkv_to_gdn_repack_f32");
26365 let cfg = LaunchConfig::for_num_elems((d_state * num_v * t) as u32);
26366 let (ds, nv, nk, kd, ti) = (
26367 d_state as i32,
26368 num_v as i32,
26369 num_k as i32,
26370 key_dim as i32,
26371 t as i32,
26372 );
26373 let __s_b = self.gpu.stream();
26374 let mut b = __s_b.launch_builder(&f);
26375 b.arg(conv_out)
26376 .arg(q_g)
26377 .arg(k_g)
26378 .arg(v_g)
26379 .arg(&ds)
26380 .arg(&nv)
26381 .arg(&nk)
26382 .arg(&kd)
26383 .arg(&ti);
26384 unsafe {
26385 b.launch(cfg)?;
26386 }
26387 Ok(())
26388 }
26389
26390 pub fn conv_left_pad(
26393 &self,
26394 src: &CudaSlice<f32>,
26395 dst: &mut CudaSlice<f32>,
26396 conv_dim: usize,
26397 t: usize,
26398 pad: usize,
26399 ) -> Result<(), Box<dyn std::error::Error>> {
26400 let f = self.func("conv_left_pad_f32");
26401 let cfg = LaunchConfig::for_num_elems((conv_dim * t) as u32);
26402 let (cd, ti, p) = (conv_dim as i32, t as i32, pad as i32);
26403 let __s_b = self.gpu.stream();
26404 let mut b = __s_b.launch_builder(&f);
26405 b.arg(src).arg(dst).arg(&cd).arg(&ti).arg(&p);
26406 unsafe {
26407 b.launch(cfg)?;
26408 }
26409 Ok(())
26410 }
26411
26412 pub fn conv_assemble_and_roll(
26416 &self,
26417 qkv_col: &CudaSlice<f32>,
26418 conv_state: &mut CudaSlice<f32>,
26419 conv_in: &mut CudaSlice<f32>,
26420 conv_dim: usize,
26421 pad: usize,
26422 ) -> Result<(), Box<dyn std::error::Error>> {
26423 let f = self.func("conv_assemble_and_roll_f32");
26424 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
26425 let (cd, p) = (conv_dim as i32, pad as i32);
26426 let __s_b = self.gpu.stream();
26427 let mut b = __s_b.launch_builder(&f);
26428 b.arg(qkv_col).arg(conv_state).arg(conv_in).arg(&cd).arg(&p);
26429 unsafe {
26430 b.launch(cfg)?;
26431 }
26432 Ok(())
26433 }
26434
26435 pub fn ssm_conv1d_fused_decode(
26441 &self,
26442 qkv_col: &CudaSlice<f32>,
26443 conv_state: &mut CudaSlice<f32>,
26444 w: &CudaSlice<f32>,
26445 conv_out: &mut CudaSlice<f32>,
26446 conv_dim: usize,
26447 d_conv: usize,
26448 ) -> Result<(), Box<dyn std::error::Error>> {
26449 let f = self.func("ssm_conv1d_fused_decode_f32");
26450 let cfg = LaunchConfig::for_num_elems(conv_dim as u32);
26451 let (cd, dc) = (conv_dim as i32, d_conv as i32);
26452 let __s_b = self.gpu.stream();
26453 let mut b = __s_b.launch_builder(&f);
26454 b.arg(qkv_col)
26455 .arg(conv_state)
26456 .arg(w)
26457 .arg(conv_out)
26458 .arg(&cd)
26459 .arg(&dc);
26460 unsafe {
26461 b.launch(cfg)?;
26462 }
26463 Ok(())
26464 }
26465
26466 pub fn slice_range(
26469 &self,
26470 src: &CudaSlice<f32>,
26471 start: usize,
26472 len: usize,
26473 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
26474 let host = self.gpu.stream().clone_dtoh(src)?;
26475 self.gpu.stream().synchronize()?;
26476 Ok(self.htod(&host[start..start + len])?)
26477 }
26478}
26479
26480#[cfg(test)]
26481mod target_dispatch_tests {
26482 use super::legacy_quant_gemm_allowed;
26483
26484 #[test]
26485 fn legacy_quant_gemm_arch_policy_honors_the_escape_hatch() {
26486 assert!(legacy_quant_gemm_allowed(false, false, false));
26488 assert!(!legacy_quant_gemm_allowed(false, false, true));
26489 assert!(!legacy_quant_gemm_allowed(true, false, false));
26491 assert!(!legacy_quant_gemm_allowed(true, false, true));
26492 assert!(legacy_quant_gemm_allowed(true, true, false));
26494 assert!(!legacy_quant_gemm_allowed(true, true, true));
26495 }
26496
26497 #[cfg(all(memra_portable_cuda, not(memra_hopper_mma)))]
26498 #[test]
26499 fn portable_build_disables_legacy_quant_gemm_without_an_env_override() {
26500 assert!(!legacy_quant_gemm_allowed(
26501 cfg!(memra_portable_cuda),
26502 cfg!(memra_hopper_mma),
26503 false
26504 ));
26505 }
26506
26507 #[cfg(memra_hopper_mma)]
26508 #[test]
26509 fn hopper_mma_build_re_admits_legacy_quant_gemm() {
26510 assert!(legacy_quant_gemm_allowed(
26511 cfg!(memra_portable_cuda),
26512 cfg!(memra_hopper_mma),
26513 false
26514 ));
26515 assert!(super::portable_mma_gated() == false);
26516 }
26517}
26518
26519impl memra_kv::KvDev for Engine {
26522 fn zeros(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
26523 Engine::zeros(self, n)
26524 }
26525 fn uninit(&self, n: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
26526 Engine::uninit(self, n)
26527 }
26528 fn alloc_u8(&self, n: usize) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
26529 Engine::alloc_u8(self, n)
26530 }
26531 fn htod_i32(&self, v: &[i32]) -> Result<CudaSlice<i32>, Box<dyn std::error::Error>> {
26532 Engine::htod_i32(self, v)
26533 }
26534 fn clone_dtod(
26535 &self,
26536 src: &CudaSlice<f32>,
26537 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
26538 Engine::clone_dtod(self, src)
26539 }
26540 fn copy_into(
26541 &self,
26542 dst: &mut CudaSlice<f32>,
26543 off: usize,
26544 src: &CudaSlice<f32>,
26545 len: usize,
26546 ) -> Result<(), Box<dyn std::error::Error>> {
26547 Engine::copy_into(self, dst, off, src, len)
26548 }
26549 fn set_i32_one(
26550 &self,
26551 d: &mut CudaSlice<i32>,
26552 v: i32,
26553 ) -> Result<(), Box<dyn std::error::Error>> {
26554 Engine::set_i32_one(self, d, v)
26555 }
26556}
26557
26558#[cfg(test)]
26559mod fused_gate_bounds_tests {
26560 use super::*;
26561
26562 #[test]
26575 #[ignore = "requires a CUDA GPU"]
26576 fn q_gate_split_refuses_a_separate_gate_wq_instead_of_reading_past_it() {
26577 let e = Engine::new(0).unwrap();
26578 let (head_dim, n_head, t) = (8usize, 4usize, 2usize);
26579 let fused = 2 * head_dim * n_head * t;
26580 let out_n = head_dim * n_head * t;
26581
26582 let narrow = e.htod(&vec![1.0f32; out_n]).unwrap();
26584 let mut q = e.uninit(out_n).unwrap();
26585 let mut gate = e.uninit(out_n).unwrap();
26586 let err = e
26587 .q_gate_split(&narrow, &mut q, &mut gate, head_dim, n_head, t)
26588 .expect_err("half-width wq must be refused, not read past")
26589 .to_string();
26590 assert!(err.contains("NO fused gate"), "{err}");
26591 assert!(err.contains(&format!("{fused}")), "{err}");
26592
26593 let host: Vec<f32> = (0..fused).map(|i| i as f32).collect();
26596 let wide = e.htod(&host).unwrap();
26597 e.q_gate_split(&wide, &mut q, &mut gate, head_dim, n_head, t)
26598 .expect("full-width wq splits");
26599 let (qh, gh) = (e.dtoh(&q).unwrap(), e.dtoh(&gate).unwrap());
26600 for tok in 0..t {
26601 for hh in 0..n_head {
26602 for d in 0..head_dim {
26603 let base = tok * (n_head * 2 * head_dim) + hh * (2 * head_dim);
26604 let idx = tok * (n_head * head_dim) + hh * head_dim + d;
26605 assert_eq!(qh[idx], host[base + d], "q t{tok} h{hh} d{d}");
26606 assert_eq!(gh[idx], host[base + head_dim + d], "gate t{tok} h{hh} d{d}");
26607 }
26608 }
26609 }
26610
26611 let mut small = e.uninit(out_n - 1).unwrap();
26613 assert!(
26614 e.q_gate_split(&wide, &mut small, &mut gate, head_dim, n_head, t)
26615 .is_err()
26616 );
26617 }
26618}
26619
26620#[cfg(test)]
26624mod fused_rope_width_tests {
26625 use super::Engine;
26626
26627 #[test]
26630 fn full_width_is_accepted() {
26631 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 256).is_ok());
26632 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_cat", 512, 512).is_ok());
26633 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append", 128, 128).is_ok());
26634 }
26635
26636 #[test]
26649 fn gemma4_official_artifact_widths_pass() {
26650 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 512, 512).is_ok());
26651 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 256, 256).is_ok());
26652 }
26653
26654 #[test]
26657 fn partial_rotary_is_refused_with_the_geometry_named() {
26658 let err = Engine::full_width_rope_only("rms_norm_qkv_rope", 64, 256)
26660 .expect_err("partial rotary must refuse");
26661 let msg = err.to_string();
26662 assert!(msg.contains("PARTIAL ROTARY REFUSED"), "{msg}");
26663 assert!(msg.contains("n_rot 64"), "{msg}");
26664 assert!(msg.contains("head_dim 256"), "{msg}");
26665 assert!(
26666 msg.contains("64..256"),
26667 "names the band it would corrupt: {msg}"
26668 );
26669 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope_append_dc", 64, 128).is_err());
26671 assert!(Engine::full_width_rope_only("rms_norm_qkv_rope", 256, 128).is_err());
26673 }
26674}