1use cudarc::driver::{CudaSlice, DevicePtr, DevicePtrMut};
19
20unsafe extern "C" {
21 fn memra_f16_pp_gemm(
23 w_f16: *const core::ffi::c_void,
24 x_f32: *const f32,
25 xh_f16: *mut core::ffi::c_void,
26 y_f32: *mut f32,
27 m: i32,
28 n: i32,
29 k: i32,
30 ws: *mut core::ffi::c_void,
31 ws_bytes: usize,
32 stream: *mut core::ffi::c_void,
33 ) -> i32;
34 fn memra_f16_cvt(
36 x_f32: *const f32,
37 xh_f16: *mut core::ffi::c_void,
38 nelem: usize,
39 stream: *mut core::ffi::c_void,
40 ) -> i32;
41 fn memra_f16_pp_gemm_pre(
43 w_f16: *const core::ffi::c_void,
44 xh_f16: *const core::ffi::c_void,
45 y_f32: *mut f32,
46 m: i32,
47 n: i32,
48 k: i32,
49 ws: *mut core::ffi::c_void,
50 ws_bytes: usize,
51 stream: *mut core::ffi::c_void,
52 ) -> i32;
53 fn memra_bf16_pp_gemm(
56 w_bf16: *const core::ffi::c_void,
57 x_f32: *const f32,
58 xb_bf16: *mut core::ffi::c_void,
59 y_f32: *mut f32,
60 m: i32,
61 n: i32,
62 k: i32,
63 ws: *mut core::ffi::c_void,
64 ws_bytes: usize,
65 stream: *mut core::ffi::c_void,
66 ) -> i32;
67 fn memra_q8_0_dequant_f16(
69 w_q8: *const core::ffi::c_void,
70 w_f16: *mut core::ffi::c_void,
71 out_f: i64,
72 nblk_row: i64,
73 stream: *mut core::ffi::c_void,
74 ) -> i32;
75 fn memra_q4_0_dequant_f16(
77 w_q4: *const core::ffi::c_void,
78 w_f16: *mut core::ffi::c_void,
79 out_f: i64,
80 nblk_row: i64,
81 stream: *mut core::ffi::c_void,
82 ) -> i32;
83 fn memra_q6_K_dequant_f16(
85 w_q6: *const core::ffi::c_void,
86 w_f16: *mut core::ffi::c_void,
87 out_f: i64,
88 nsb_row: i64,
89 stream: *mut core::ffi::c_void,
90 ) -> i32;
91 fn memra_q4_K_dequant_f16(
93 w_q4k: *const core::ffi::c_void,
94 w_f16: *mut core::ffi::c_void,
95 out_f: i64,
96 nsb_row: i64,
97 stream: *mut core::ffi::c_void,
98 ) -> i32;
99 fn memra_q5_K_dequant_f16(
101 w_q5k: *const core::ffi::c_void,
102 w_f16: *mut core::ffi::c_void,
103 out_f: i64,
104 nsb_row: i64,
105 stream: *mut core::ffi::c_void,
106 ) -> i32;
107}
108
109pub fn pp_f16_enabled() -> bool {
115 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
116 *ON.get_or_init(|| match std::env::var("MEMRA_PP_F16").as_deref() {
117 Ok("1") => true,
118 Ok("0") => false,
119 _ => cfg!(memra_hopper_mma),
120 })
121}
122
123pub fn pp_f16_capacity_ok(free: usize, need: usize) -> bool {
132 if std::env::var("MEMRA_PP_F16").is_ok() {
133 return false; }
135 need > 0 && free >= need + (8usize << 30)
136}
137
138pub fn pp_bf16_enabled() -> bool {
150 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
151 *ON.get_or_init(|| {
152 let on = matches!(std::env::var("MEMRA_PP_BF16").as_deref(), Ok("1"));
153 eprintln!(
159 "[bf16-tc] flag={} (MEMRA_PP_BF16; engagement is the per-shape ENGAGED line + \
160 the dispatch counter)",
161 if on { "on" } else { "off" }
162 );
163 on
164 })
165}
166
167pub static BF16_TC_DISPATCHES: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
174
175pub fn bf16_tc_dispatches() -> u64 {
178 BF16_TC_DISPATCHES.load(std::sync::atomic::Ordering::Relaxed)
179}
180
181pub struct F16Scratch {
184 pub xh: CudaSlice<u8>,
185 pub ws: CudaSlice<u8>,
186 cap_xh: usize,
187}
188
189impl F16Scratch {
190 pub fn with_capacity(
193 e: &crate::Engine,
194 xh_bytes: usize,
195 ) -> Result<Self, Box<dyn std::error::Error>> {
196 Ok(F16Scratch {
197 xh: e.alloc_u8_uninit(xh_bytes)?,
198 ws: e.alloc_u8_uninit(F16_WS_BYTES)?,
199 cap_xh: xh_bytes,
200 })
201 }
202}
203
204pub(crate) const F16_WS_BYTES: usize = 64 << 20;
205
206impl crate::Engine {
207 pub fn f16_scratch_swap(&self, new: Option<F16Scratch>) -> Option<F16Scratch> {
210 std::mem::replace(&mut *self.f16_scratch.lock().unwrap(), new)
211 }
212
213 pub fn try_f16_gemm(
216 &self,
217 w: &crate::model::GpuTensor,
218 x: &CudaSlice<f32>,
219 m: usize,
220 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
221 use crate::model::GpuTensor;
222 let (w16, ne, scale) = match w {
223 GpuTensor::Quant {
224 f16: Some(w16),
225 ne,
226 scale,
227 ..
228 } => (w16, ne, *scale),
229 _ => return Ok(None),
230 };
231 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
232 let mut y = self.qmatvec_gemm_f16_raw(w16, x, m, in_f, out_f)?;
233 if scale != 1.0 {
234 self.scale_inplace(&mut y, scale, m * out_f)?;
235 }
236 Ok(Some(y))
237 }
238
239 pub fn bf16_tc_gemm(
243 &self,
244 data: &CudaSlice<u8>,
245 x: &CudaSlice<f32>,
246 m: usize,
247 in_f: usize,
248 out_f: usize,
249 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
250 let need_xh = m * in_f * 2;
251 let mut guard = self.f16_scratch.lock().unwrap();
252 if guard.is_none() {
253 *guard = Some(F16Scratch {
254 xh: self.alloc_u8_uninit(need_xh)?,
255 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
256 cap_xh: need_xh,
257 });
258 }
259 let s = guard.as_mut().unwrap();
260 if need_xh > s.cap_xh {
261 s.xh = self.alloc_u8_uninit(need_xh)?;
262 s.cap_xh = need_xh;
263 }
264 let mut y = self.uninit(m * out_f)?; let rc = {
266 let stream = self.gpu.stream();
267 let (w_p, _gw) = data.device_ptr(&stream);
268 let (x_p, _gx) = x.device_ptr(&stream);
269 let (h_p, _gh) = s.xh.device_ptr_mut(&stream);
270 let (y_p, _gy) = y.device_ptr_mut(&stream);
271 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
272 if !(w_p as usize).is_multiple_of(16) {
276 -1
277 } else {
278 unsafe {
279 memra_bf16_pp_gemm(
280 w_p as *const core::ffi::c_void,
281 x_p as *const f32,
282 h_p as *mut core::ffi::c_void,
283 y_p as *mut f32,
284 m as i32,
285 out_f as i32,
286 in_f as i32,
287 ws_p as *mut core::ffi::c_void,
288 F16_WS_BYTES,
289 stream.cu_stream() as *mut core::ffi::c_void,
290 )
291 }
292 }
293 };
294 if rc != 0 {
301 static SAID: std::sync::Mutex<
302 Option<std::collections::HashSet<(usize, usize, usize)>>,
303 > = std::sync::Mutex::new(None);
304 let mut g = SAID.lock().unwrap();
305 let seen = g.get_or_insert_with(std::collections::HashSet::new);
306 if seen.insert((m, out_f, in_f)) {
307 eprintln!(
308 "[bf16-tc] DECLINED m={m} n={out_f} k={in_f} rc={rc} \
309 (1xxxx=cudaError convert, 2xxxx=no cublasLt algo, 3xxxx=matmul status, \
310 4xxxx=cublasLtCreate, -1=weight not 16B-aligned) — this shape falls back to \
311 the f32 dequant GEMM; every other shape keeps the tensor-core path"
312 );
313 }
314 return Ok(None);
315 }
316 BF16_TC_DISPATCHES.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
322 {
323 static ACCEPTED: std::sync::Mutex<
324 Option<std::collections::HashSet<(usize, usize, usize)>>,
325 > = std::sync::Mutex::new(None);
326 let mut g = ACCEPTED.lock().unwrap();
327 let seen = g.get_or_insert_with(std::collections::HashSet::new);
328 if seen.insert((m, out_f, in_f)) {
329 eprintln!(
330 "[bf16-tc] ENGAGED m={m} n={out_f} k={in_f} (bf16 tensor-core GEMM on resident checkpoint bytes)"
331 );
332 }
333 }
334 Ok(Some(y))
335 }
336
337 pub fn bf16_gemv_lt_into(
351 &self,
352 data: &CudaSlice<u8>,
353 x: &CudaSlice<f32>,
354 y: &mut CudaSlice<f32>,
355 in_f: usize,
356 out_f: usize,
357 t: usize,
358 ) -> Result<bool, Box<dyn std::error::Error>> {
359 if t == 0 || x.len() < t * in_f || y.len() < t * out_f {
360 return Err("bf16_gemv_lt geometry".into());
361 }
362 if data.len() < out_f * in_f * 2 {
363 return Err("bf16_gemv_lt weight too small".into());
364 }
365 let need_xh = t * in_f * 2;
366 let mut guard = self.f16_scratch.lock().unwrap();
367 if guard.is_none() {
368 *guard = Some(F16Scratch {
369 xh: self.alloc_u8_uninit(need_xh)?,
370 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
371 cap_xh: need_xh,
372 });
373 }
374 let s = guard.as_mut().unwrap();
375 if need_xh > s.cap_xh {
376 s.xh = self.alloc_u8_uninit(need_xh)?;
377 s.cap_xh = need_xh;
378 }
379 let rc = {
380 let stream = self.gpu.stream();
381 let (w_p, _gw) = data.device_ptr(&stream);
382 let (x_p, _gx) = x.device_ptr(&stream);
383 let (h_p, _gh) = s.xh.device_ptr_mut(&stream);
384 let (y_p, _gy) = y.device_ptr_mut(&stream);
385 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
386 if !(w_p as usize).is_multiple_of(16) {
387 -1
388 } else {
389 unsafe {
390 memra_bf16_pp_gemm(
391 w_p as *const core::ffi::c_void,
392 x_p as *const f32,
393 h_p as *mut core::ffi::c_void,
394 y_p as *mut f32,
395 t as i32,
396 out_f as i32,
397 in_f as i32,
398 ws_p as *mut core::ffi::c_void,
399 F16_WS_BYTES,
400 stream.cu_stream() as *mut core::ffi::c_void,
401 )
402 }
403 }
404 };
405 if rc != 0 {
406 static SAID: std::sync::Mutex<
407 Option<std::collections::HashSet<(usize, usize, usize)>>,
408 > = std::sync::Mutex::new(None);
409 let mut g = SAID.lock().unwrap();
410 let seen = g.get_or_insert_with(std::collections::HashSet::new);
411 if seen.insert((t, out_f, in_f)) {
412 eprintln!(
413 "[b200-bf16-gemv-lt] DECLINED t={t} n={out_f} k={in_f} rc={rc} \
414 (1xxxx=cudaError convert, 2xxxx=no cublasLt algo, 3xxxx=matmul status, \
415 4xxxx=cublasLtCreate, -1=weight not 16B-aligned) — this shape keeps the \
416 shipped matvec kernel"
417 );
418 }
419 return Ok(false);
420 }
421 {
422 static ACCEPTED: std::sync::Mutex<
423 Option<std::collections::HashSet<(usize, usize, usize)>>,
424 > = std::sync::Mutex::new(None);
425 let mut g = ACCEPTED.lock().unwrap();
426 let seen = g.get_or_insert_with(std::collections::HashSet::new);
427 if seen.insert((t, out_f, in_f)) {
428 eprintln!(
429 "[b200-bf16-gemv-lt] ENGAGED t={t} n={out_f} k={in_f} (cuBLASLt reference \
430 GEMV, numeric class bf16_gemv_lt, MEMRA_B200_BF16_GEMV_LT=1)"
431 );
432 }
433 }
434 Ok(true)
435 }
436
437 pub fn qmatvec_gemm_f16_raw(
439 &self,
440 w16: &CudaSlice<u8>,
441 x: &CudaSlice<f32>,
442 m: usize,
443 in_f: usize,
444 out_f: usize,
445 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
446 let need_xh = m * in_f * 2;
447 let mut guard = self.f16_scratch.lock().unwrap();
448 if guard.is_none() {
449 *guard = Some(F16Scratch {
450 xh: self.alloc_u8_uninit(need_xh)?,
451 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
452 cap_xh: need_xh,
453 });
454 }
455 let s = guard.as_mut().unwrap();
456 if need_xh > s.cap_xh {
457 s.xh = self.alloc_u8_uninit(need_xh)?;
458 s.cap_xh = need_xh;
459 }
460 let mut y = self.uninit(m * out_f)?; let rc = {
462 let stream = self.gpu.stream();
463 let (w_p, _gw) = w16.device_ptr(&stream);
464 let (x_p, _gx) = x.device_ptr(&stream);
465 let (h_p, _gh) = s.xh.device_ptr_mut(&stream);
466 let (y_p, _gy) = y.device_ptr_mut(&stream);
467 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
468 unsafe {
469 memra_f16_pp_gemm(
470 w_p as *const core::ffi::c_void,
471 x_p as *const f32,
472 h_p as *mut core::ffi::c_void,
473 y_p as *mut f32,
474 m as i32,
475 out_f as i32,
476 in_f as i32,
477 ws_p as *mut core::ffi::c_void,
478 F16_WS_BYTES,
479 stream.cu_stream() as *mut core::ffi::c_void,
480 )
481 }
482 };
483 if rc != 0 {
484 return Err(format!(
485 "memra_f16_pp_gemm rc={rc} (m={m} n={out_f} k={in_f}; 1xxxx=cudaError convert, \
486 2xxxx=no cublasLt algo, 3xxxx=matmul status)"
487 )
488 .into());
489 }
490 Ok(y)
491 }
492
493 pub fn f16_act(
497 &self,
498 x: &CudaSlice<f32>,
499 nelem: usize,
500 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
501 let mut xh = self.alloc_u8_uninit(nelem * 2)?;
502 let rc = {
503 let stream = self.gpu.stream();
504 let (x_p, _gx) = x.device_ptr(&stream);
505 let (h_p, _gh) = xh.device_ptr_mut(&stream);
506 unsafe {
507 memra_f16_cvt(
508 x_p as *const f32,
509 h_p as *mut core::ffi::c_void,
510 nelem,
511 stream.cu_stream() as *mut core::ffi::c_void,
512 )
513 }
514 };
515 if rc != 0 {
516 return Err(format!("memra_f16_cvt rc={rc}").into());
517 }
518 Ok(xh)
519 }
520
521 pub fn try_f16_gemm_pre_into(
526 &self,
527 w: &crate::model::GpuTensor,
528 xh: &CudaSlice<u8>,
529 m: usize,
530 y: &mut CudaSlice<f32>,
531 ) -> Result<bool, Box<dyn std::error::Error>> {
532 use crate::model::GpuTensor;
533 let (w16, ne, scale) = match w {
534 GpuTensor::Quant {
535 f16: Some(w16),
536 ne,
537 scale,
538 ..
539 } => (w16, ne, *scale),
540 _ => return Ok(false),
541 };
542 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
543 assert!(
544 y.len() >= m * out_f,
545 "try_f16_gemm_pre_into: output slab too small"
546 );
547 let mut guard = self.f16_scratch.lock().unwrap();
548 if guard.is_none() {
549 *guard = Some(F16Scratch {
550 xh: self.alloc_u8_uninit(2)?,
551 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
552 cap_xh: 2,
553 });
554 }
555 let s = guard.as_mut().unwrap();
556 let rc = {
557 let stream = self.gpu.stream();
558 let (w_p, _gw) = w16.device_ptr(&stream);
559 let (h_p, _gh) = xh.device_ptr(&stream);
560 let (y_p, _gy) = y.device_ptr_mut(&stream);
561 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
562 unsafe {
563 memra_f16_pp_gemm_pre(
564 w_p as *const core::ffi::c_void,
565 h_p as *const core::ffi::c_void,
566 y_p as *mut f32,
567 m as i32,
568 out_f as i32,
569 in_f as i32,
570 ws_p as *mut core::ffi::c_void,
571 F16_WS_BYTES,
572 stream.cu_stream() as *mut core::ffi::c_void,
573 )
574 }
575 };
576 if rc != 0 {
577 return Err(
578 format!("memra_f16_pp_gemm_pre(into) rc={rc} (m={m} n={out_f} k={in_f})").into(),
579 );
580 }
581 if scale != 1.0 {
582 self.scale_inplace(y, scale, m * out_f)?;
583 }
584 Ok(true)
585 }
586
587 pub fn try_f16_gemm_pre_into_off(
591 &self,
592 w: &crate::model::GpuTensor,
593 xh: &CudaSlice<u8>,
594 m: usize,
595 y: &mut CudaSlice<f32>,
596 off_elems: usize,
597 ) -> Result<bool, Box<dyn std::error::Error>> {
598 use crate::model::GpuTensor;
599 let (w16, ne, scale) = match w {
600 GpuTensor::Quant {
601 f16: Some(w16),
602 ne,
603 scale,
604 ..
605 } => (w16, ne, *scale),
606 _ => return Ok(false),
607 };
608 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
609 assert!(
610 y.len() >= off_elems + m * out_f,
611 "try_f16_gemm_pre_into_off: output slab too small"
612 );
613 if scale != 1.0 {
614 return Ok(false); }
616 let mut guard = self.f16_scratch.lock().unwrap();
617 if guard.is_none() {
618 *guard = Some(F16Scratch {
619 xh: self.alloc_u8_uninit(2)?,
620 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
621 cap_xh: 2,
622 });
623 }
624 let s = guard.as_mut().unwrap();
625 let rc = {
626 let stream = self.gpu.stream();
627 let (w_p, _gw) = w16.device_ptr(&stream);
628 let (h_p, _gh) = xh.device_ptr(&stream);
629 let (y_p, _gy) = y.device_ptr_mut(&stream);
630 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
631 unsafe {
632 memra_f16_pp_gemm_pre(
633 w_p as *const core::ffi::c_void,
634 h_p as *const core::ffi::c_void,
635 (y_p as *mut f32).add(off_elems),
636 m as i32,
637 out_f as i32,
638 in_f as i32,
639 ws_p as *mut core::ffi::c_void,
640 F16_WS_BYTES,
641 stream.cu_stream() as *mut core::ffi::c_void,
642 )
643 }
644 };
645 if rc != 0 {
646 return Err(format!(
647 "memra_f16_pp_gemm_pre(into_off) rc={rc} (m={m} n={out_f} k={in_f})"
648 )
649 .into());
650 }
651 Ok(true)
652 }
653
654 pub fn try_f16_gemm_pre(
657 &self,
658 w: &crate::model::GpuTensor,
659 xh: &CudaSlice<u8>,
660 m: usize,
661 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
662 use crate::model::GpuTensor;
663 let (w16, ne, scale) = match w {
664 GpuTensor::Quant {
665 f16: Some(w16),
666 ne,
667 scale,
668 ..
669 } => (w16, ne, *scale),
670 _ => return Ok(None),
671 };
672 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
673 let mut guard = self.f16_scratch.lock().unwrap();
675 if guard.is_none() {
676 *guard = Some(F16Scratch {
677 xh: self.alloc_u8_uninit(2)?,
678 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
679 cap_xh: 2,
680 });
681 }
682 let s = guard.as_mut().unwrap();
683 let mut y = self.uninit(m * out_f)?;
684 let rc = {
685 let stream = self.gpu.stream();
686 let (w_p, _gw) = w16.device_ptr(&stream);
687 let (h_p, _gh) = xh.device_ptr(&stream);
688 let (y_p, _gy) = y.device_ptr_mut(&stream);
689 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
690 unsafe {
691 memra_f16_pp_gemm_pre(
692 w_p as *const core::ffi::c_void,
693 h_p as *const core::ffi::c_void,
694 y_p as *mut f32,
695 m as i32,
696 out_f as i32,
697 in_f as i32,
698 ws_p as *mut core::ffi::c_void,
699 F16_WS_BYTES,
700 stream.cu_stream() as *mut core::ffi::c_void,
701 )
702 }
703 };
704 if rc != 0 {
705 return Err(format!("memra_f16_pp_gemm_pre rc={rc} (m={m} n={out_f} k={in_f})").into());
706 }
707 if scale != 1.0 {
708 self.scale_inplace(&mut y, scale, m * out_f)?;
709 }
710 Ok(Some(y))
711 }
712
713 pub fn build_q8_f16_raw(
716 &self,
717 bytes: &CudaSlice<u8>,
718 in_f: usize,
719 out_f: usize,
720 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
721 assert!(in_f.is_multiple_of(32));
722 let nblk = in_f / 32;
723 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
724 let rc = {
725 let stream = self.gpu.stream();
726 let (s_p, _gs) = bytes.device_ptr(&stream);
727 let (d_p, _gd) = dst.device_ptr_mut(&stream);
728 unsafe {
729 memra_q8_0_dequant_f16(
730 s_p as *const core::ffi::c_void,
731 d_p as *mut core::ffi::c_void,
732 out_f as i64,
733 nblk as i64,
734 stream.cu_stream() as *mut core::ffi::c_void,
735 )
736 }
737 };
738 if rc != 0 {
739 return Err(format!("memra_q8_0_dequant_f16 rc={rc}").into());
740 }
741 Ok(dst)
742 }
743
744 pub fn build_q8_f16(
747 &self,
748 t: &mut crate::model::GpuTensor,
749 ) -> Result<(), Box<dyn std::error::Error>> {
750 use crate::model::GpuTensor;
751 let GpuTensor::Quant {
752 bytes,
753 qtype,
754 row_bytes,
755 ne,
756 f16,
757 ..
758 } = t
759 else {
760 return Ok(());
761 };
762 let q4 = *qtype == crate::QT_Q4_0;
770 let q6k = *qtype == crate::QT_Q6_K;
771 let q4k = *qtype == crate::QT_Q4_K;
772 let q5k = *qtype == crate::QT_Q5_K;
773 if (*qtype != crate::QT_Q8_0 && !q4 && !q6k && !q4k && !q5k)
774 || f16.is_some()
775 || ne.len() != 2
776 {
777 return Ok(());
778 }
779 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
780 if q6k || q4k || q5k {
781 let sb = if q6k {
782 210
783 } else if q5k {
784 176
785 } else {
786 144
787 };
788 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * sb {
789 return Ok(());
790 }
791 } else if in_f % 32 != 0 || *row_bytes != (in_f / 32) * (if q4 { 18 } else { 34 }) {
792 return Ok(());
793 }
794 use std::sync::atomic::{AtomicUsize, Ordering};
797 static SPENT: AtomicUsize = AtomicUsize::new(0);
798 static BUDGET: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
799 let budget = *BUDGET.get_or_init(|| {
800 std::env::var("MEMRA_PP_F16_BUDGET_MB")
801 .ok()
802 .and_then(|v| v.parse::<usize>().ok())
803 .unwrap_or(32768)
804 << 20
805 });
806 let sz = out_f * in_f * 2;
807 if SPENT.fetch_add(sz, Ordering::Relaxed) + sz > budget {
808 SPENT.fetch_sub(sz, Ordering::Relaxed);
809 return Ok(());
810 }
811 let mirror = if q6k {
812 self.build_q6k_f16_raw(bytes, in_f, out_f)?
813 } else if q4k {
814 self.build_q4k_f16_raw(bytes, in_f, out_f)?
815 } else if q5k {
816 self.build_q5k_f16_raw(bytes, in_f, out_f)?
817 } else if q4 {
818 self.build_q4_f16_raw(bytes, in_f, out_f)?
819 } else {
820 self.build_q8_f16_raw(bytes, in_f, out_f)?
821 };
822 *f16 = Some(mirror);
823 Ok(())
824 }
825
826 pub fn build_q4_f16_raw(
828 &self,
829 bytes: &CudaSlice<u8>,
830 in_f: usize,
831 out_f: usize,
832 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
833 assert!(in_f.is_multiple_of(32));
834 let nblk = in_f / 32;
835 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
836 let rc = {
837 let stream = self.gpu.stream();
838 let (s_p, _gs) = bytes.device_ptr(&stream);
839 let (d_p, _gd) = dst.device_ptr_mut(&stream);
840 unsafe {
841 memra_q4_0_dequant_f16(
842 s_p as *const core::ffi::c_void,
843 d_p as *mut core::ffi::c_void,
844 out_f as i64,
845 nblk as i64,
846 stream.cu_stream() as *mut core::ffi::c_void,
847 )
848 }
849 };
850 if rc != 0 {
851 return Err(format!("memra_q4_0_dequant_f16 rc={rc}").into());
852 }
853 Ok(dst)
854 }
855
856 pub fn build_q5k_f16_raw(
859 &self,
860 bytes: &CudaSlice<u8>,
861 in_f: usize,
862 out_f: usize,
863 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
864 assert!(in_f.is_multiple_of(256));
865 let nsb = in_f / 256;
866 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
867 let rc = {
868 let stream = self.gpu.stream();
869 let (s_p, _gs) = bytes.device_ptr(&stream);
870 let (d_p, _gd) = dst.device_ptr_mut(&stream);
871 unsafe {
872 memra_q5_K_dequant_f16(
873 s_p as *const core::ffi::c_void,
874 d_p as *mut core::ffi::c_void,
875 out_f as i64,
876 nsb as i64,
877 stream.cu_stream() as *mut core::ffi::c_void,
878 )
879 }
880 };
881 if rc != 0 {
882 return Err(format!("memra_q5_K_dequant_f16 rc={rc}").into());
883 }
884 Ok(dst)
885 }
886
887 pub fn build_q4k_f16_raw(
890 &self,
891 bytes: &CudaSlice<u8>,
892 in_f: usize,
893 out_f: usize,
894 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
895 assert!(in_f.is_multiple_of(256));
896 let nsb = in_f / 256;
897 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
898 let rc = {
899 let stream = self.gpu.stream();
900 let (s_p, _gs) = bytes.device_ptr(&stream);
901 let (d_p, _gd) = dst.device_ptr_mut(&stream);
902 unsafe {
903 memra_q4_K_dequant_f16(
904 s_p as *const core::ffi::c_void,
905 d_p as *mut core::ffi::c_void,
906 out_f as i64,
907 nsb as i64,
908 stream.cu_stream() as *mut core::ffi::c_void,
909 )
910 }
911 };
912 if rc != 0 {
913 return Err(format!("memra_q4_K_dequant_f16 rc={rc}").into());
914 }
915 Ok(dst)
916 }
917
918 pub fn build_q6k_f16_raw(
919 &self,
920 bytes: &CudaSlice<u8>,
921 in_f: usize,
922 out_f: usize,
923 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
924 assert!(in_f.is_multiple_of(256));
925 let nsb = in_f / 256;
926 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
927 let rc = {
928 let stream = self.gpu.stream();
929 let (s_p, _gs) = bytes.device_ptr(&stream);
930 let (d_p, _gd) = dst.device_ptr_mut(&stream);
931 unsafe {
932 memra_q6_K_dequant_f16(
933 s_p as *const core::ffi::c_void,
934 d_p as *mut core::ffi::c_void,
935 out_f as i64,
936 nsb as i64,
937 stream.cu_stream() as *mut core::ffi::c_void,
938 )
939 }
940 };
941 if rc != 0 {
942 return Err(format!("memra_q6_K_dequant_f16 rc={rc}").into());
943 }
944 Ok(dst)
945 }
946}