1use anyhow::{Result, bail};
11use std::ffi::c_void;
12use std::sync::OnceLock;
13
14mod fp8;
17pub use fp8::{fp8_gemm_act_weight_t_blkscaled, fp8_gemm_act_weight_t_rowwise};
18
19#[allow(non_camel_case_types)]
20type cublasLtHandle_t = *mut c_void;
21#[allow(non_camel_case_types)]
22type cublasLtMatmulDesc_t = *mut c_void;
23#[allow(non_camel_case_types)]
24type cublasLtMatrixLayout_t = *mut c_void;
25#[allow(non_camel_case_types)]
26type cublasLtMatmulPreference_t = *mut c_void;
27
28const CUDA_R_16BF: i32 = 14;
29const CUDA_R_32F: i32 = 0;
30const CUDA_R_8F_E4M3: i32 = 28;
31const CUBLAS_COMPUTE_32F: i32 = 68;
32const CUBLAS_OP_N: i32 = 0;
33const CUBLAS_OP_T: i32 = 1;
34const DESC_TRANSA: u32 = 3;
35const DESC_TRANSB: u32 = 4;
36const DESC_A_SCALE_POINTER: u32 = 17;
37const DESC_B_SCALE_POINTER: u32 = 18;
38const DESC_A_SCALE_MODE: u32 = 31;
39const DESC_B_SCALE_MODE: u32 = 32;
40const SCALE_MODE_OUTER_VEC_32F: i32 = 3;
41const SCALE_MODE_VEC128_32F: i32 = 4;
42const SCALE_MODE_BLK128X128_32F: i32 = 5;
43const PREF_MAX_WORKSPACE_BYTES: u32 = 1;
44
45unsafe extern "C" {
46 fn cublasLtCreate(handle: *mut cublasLtHandle_t) -> i32;
47 fn cublasLtMatmulDescCreate(
48 desc: *mut cublasLtMatmulDesc_t,
49 compute_type: i32,
50 scale_type: i32,
51 ) -> i32;
52 fn cublasLtMatmulDescSetAttribute(
53 desc: cublasLtMatmulDesc_t,
54 attr: u32,
55 buf: *const c_void,
56 size: usize,
57 ) -> i32;
58 fn cublasLtMatmulDescDestroy(desc: cublasLtMatmulDesc_t) -> i32;
59 fn cublasLtMatrixLayoutCreate(
60 layout: *mut cublasLtMatrixLayout_t,
61 dtype: i32,
62 rows: u64,
63 cols: u64,
64 ld: i64,
65 ) -> i32;
66 fn cublasLtMatrixLayoutDestroy(layout: cublasLtMatrixLayout_t) -> i32;
67 fn cublasLtMatmulPreferenceCreate(pref: *mut cublasLtMatmulPreference_t) -> i32;
68 fn cublasLtMatmulPreferenceSetAttribute(
69 pref: cublasLtMatmulPreference_t,
70 attr: u32,
71 buf: *const c_void,
72 size: usize,
73 ) -> i32;
74 fn cublasLtMatmulPreferenceDestroy(pref: cublasLtMatmulPreference_t) -> i32;
75 #[allow(clippy::too_many_arguments)]
76 fn cublasLtMatmulAlgoGetHeuristic(
77 handle: cublasLtHandle_t,
78 desc: cublasLtMatmulDesc_t,
79 a: cublasLtMatrixLayout_t,
80 b: cublasLtMatrixLayout_t,
81 c: cublasLtMatrixLayout_t,
82 d: cublasLtMatrixLayout_t,
83 pref: cublasLtMatmulPreference_t,
84 requested: i32,
85 results: *mut c_void,
86 returned: *mut i32,
87 ) -> i32;
88 #[allow(clippy::too_many_arguments)]
89 fn cublasLtMatmul(
90 handle: cublasLtHandle_t,
91 desc: cublasLtMatmulDesc_t,
92 alpha: *const c_void,
93 a: *const c_void,
94 layout_a: cublasLtMatrixLayout_t,
95 b: *const c_void,
96 layout_b: cublasLtMatrixLayout_t,
97 beta: *const c_void,
98 c: *const c_void,
99 layout_c: cublasLtMatrixLayout_t,
100 d: *mut c_void,
101 layout_d: cublasLtMatrixLayout_t,
102 algo: *const c_void,
103 workspace: *mut c_void,
104 workspace_size: usize,
105 stream: *mut c_void,
106 ) -> i32;
107 fn cuMemAlloc_v2(dptr: *mut u64, bytesize: usize) -> i32;
108 fn cuMemFree_v2(dptr: u64) -> i32;
109 fn cuStreamSynchronize(stream: u64) -> i32;
110}
111
112struct Ctx {
113 handle: cublasLtHandle_t,
114 workspace: u64,
115 ws_size: usize,
116}
117unsafe impl Send for Ctx {}
120unsafe impl Sync for Ctx {}
121
122static CTX: OnceLock<Ctx> = OnceLock::new();
133
134fn ctx() -> Result<&'static Ctx> {
135 if let Some(c) = CTX.get() {
136 return Ok(c);
137 }
138 let mut handle: cublasLtHandle_t = std::ptr::null_mut();
139 let st = unsafe { cublasLtCreate(&mut handle) };
140 if st != 0 {
141 bail!("cublasLtCreate failed: {st}");
142 }
143 let ws_size = 64 * 1024 * 1024;
144 let mut ws: u64 = 0;
145 let st = unsafe { cuMemAlloc_v2(&mut ws, ws_size) };
146 if st != 0 {
147 bail!("cuMemAlloc cuBLASLt workspace failed: {st}");
148 }
149 let _ = CTX.set(Ctx {
150 handle,
151 workspace: ws,
152 ws_size,
153 });
154 Ok(CTX.get().unwrap())
155}
156
157pub fn prewarm(stream: u64) {
170 let r = (|| -> Result<()> {
171 let bytes = 64usize * 64 * 2;
172 let mut a = 0u64;
173 let mut b = 0u64;
174 let mut d = 0u64;
175 unsafe {
176 chk(cuMemAlloc_v2(&mut a, bytes), "prewarm alloc a")?;
177 chk(cuMemAlloc_v2(&mut b, bytes), "prewarm alloc b")?;
178 chk(cuMemAlloc_v2(&mut d, bytes), "prewarm alloc d")?;
179 }
180 let res = bf16_gemm_act_weight_t(a, b, d, 64, 64, 64, stream);
181 unsafe {
182 chk(cuStreamSynchronize(stream), "prewarm sync")?;
183 let _ = cuMemFree_v2(a);
184 let _ = cuMemFree_v2(b);
185 let _ = cuMemFree_v2(d);
186 }
187 res
188 })();
189 match r {
190 Ok(()) => tracing::info!("cuBLASLt pre-warmed (handle + workspace + kernel images)"),
191 Err(e) => tracing::warn!("cuBLASLt pre-warm failed (request 1 pays lazy init): {e}"),
192 }
193}
194
195fn chk(status: i32, what: &str) -> Result<()> {
196 if status != 0 {
197 bail!("cuBLASLt {what} failed: status {status}");
198 }
199 Ok(())
200}
201
202pub fn bf16_gemm_act_weight_t(
206 act: u64,
207 weight: u64,
208 out: u64,
209 m: u32,
210 n: u32,
211 k: u32,
212 stream: u64,
213) -> Result<()> {
214 let ctx = ctx()?;
215 unsafe {
216 let mut desc: cublasLtMatmulDesc_t = std::ptr::null_mut();
217 chk(
218 cublasLtMatmulDescCreate(&mut desc, CUBLAS_COMPUTE_32F, CUDA_R_32F),
219 "DescCreate",
220 )?;
221 let ta = CUBLAS_OP_T;
222 let tb = CUBLAS_OP_N;
223 chk(
224 cublasLtMatmulDescSetAttribute(
225 desc,
226 DESC_TRANSA,
227 &ta as *const i32 as *const c_void,
228 4,
229 ),
230 "TRANSA",
231 )?;
232 chk(
233 cublasLtMatmulDescSetAttribute(
234 desc,
235 DESC_TRANSB,
236 &tb as *const i32 as *const c_void,
237 4,
238 ),
239 "TRANSB",
240 )?;
241 let mut la: cublasLtMatrixLayout_t = std::ptr::null_mut();
245 let mut lb: cublasLtMatrixLayout_t = std::ptr::null_mut();
246 let mut ld_: cublasLtMatrixLayout_t = std::ptr::null_mut();
247 chk(
248 cublasLtMatrixLayoutCreate(&mut la, CUDA_R_16BF, k as u64, n as u64, k as i64),
249 "LayoutA",
250 )?;
251 chk(
252 cublasLtMatrixLayoutCreate(&mut lb, CUDA_R_16BF, k as u64, m as u64, k as i64),
253 "LayoutB",
254 )?;
255 chk(
256 cublasLtMatrixLayoutCreate(&mut ld_, CUDA_R_16BF, n as u64, m as u64, n as i64),
257 "LayoutD",
258 )?;
259 let mut pref: cublasLtMatmulPreference_t = std::ptr::null_mut();
260 chk(cublasLtMatmulPreferenceCreate(&mut pref), "PrefCreate")?;
261 let ws_size = ctx.ws_size;
262 chk(
263 cublasLtMatmulPreferenceSetAttribute(
264 pref,
265 PREF_MAX_WORKSPACE_BYTES,
266 &ws_size as *const usize as *const c_void,
267 std::mem::size_of::<usize>(),
268 ),
269 "PrefWorkspace",
270 )?;
271 let mut result = [0u8; 128];
274 let mut returned: i32 = 0;
275 chk(
276 cublasLtMatmulAlgoGetHeuristic(
277 ctx.handle,
278 desc,
279 la,
280 lb,
281 ld_,
282 ld_,
283 pref,
284 1,
285 result.as_mut_ptr() as *mut c_void,
286 &mut returned,
287 ),
288 "AlgoGetHeuristic",
289 )?;
290 if returned < 1 {
291 bail!("cuBLASLt: no algorithm for {m}x{n}x{k}");
292 }
293 let alpha: f32 = 1.0;
294 let beta: f32 = 0.0;
295 let status = cublasLtMatmul(
296 ctx.handle,
297 desc,
298 &alpha as *const f32 as *const c_void,
299 weight as *const c_void,
300 la,
301 act as *const c_void,
302 lb,
303 &beta as *const f32 as *const c_void,
304 out as *const c_void,
305 ld_,
306 out as *mut c_void,
307 ld_,
308 result.as_ptr() as *const c_void,
309 ctx.workspace as *mut c_void,
310 ctx.ws_size,
311 stream as *mut c_void,
312 );
313 cublasLtMatmulPreferenceDestroy(pref);
314 cublasLtMatrixLayoutDestroy(la);
315 cublasLtMatrixLayoutDestroy(lb);
316 cublasLtMatrixLayoutDestroy(ld_);
317 cublasLtMatmulDescDestroy(desc);
318 chk(status, "Matmul")?;
319 }
320 Ok(())
321}