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(|| matches!(std::env::var("MEMRA_PP_BF16").as_deref(), Ok("1")))
152}
153
154pub struct F16Scratch {
157 pub xh: CudaSlice<u8>,
158 pub ws: CudaSlice<u8>,
159 cap_xh: usize,
160}
161
162impl F16Scratch {
163 pub fn with_capacity(
166 e: &crate::Engine,
167 xh_bytes: usize,
168 ) -> Result<Self, Box<dyn std::error::Error>> {
169 Ok(F16Scratch {
170 xh: e.alloc_u8_uninit(xh_bytes)?,
171 ws: e.alloc_u8_uninit(F16_WS_BYTES)?,
172 cap_xh: xh_bytes,
173 })
174 }
175}
176
177const F16_WS_BYTES: usize = 64 << 20;
178
179impl crate::Engine {
180 pub fn f16_scratch_swap(&self, new: Option<F16Scratch>) -> Option<F16Scratch> {
183 std::mem::replace(&mut *self.f16_scratch.lock().unwrap(), new)
184 }
185
186 pub fn try_f16_gemm(
189 &self,
190 w: &crate::model::GpuTensor,
191 x: &CudaSlice<f32>,
192 m: usize,
193 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
194 use crate::model::GpuTensor;
195 let (w16, ne, scale) = match w {
196 GpuTensor::Quant {
197 f16: Some(w16),
198 ne,
199 scale,
200 ..
201 } => (w16, ne, *scale),
202 _ => return Ok(None),
203 };
204 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
205 static SIM_ACT: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
210 let sim_act =
211 *SIM_ACT.get_or_init(|| std::env::var("MEMRA_W8A8_SIM").as_deref() == Ok("2"));
212 let mut y = if sim_act {
213 let mut hx = self.dtoh(x)?;
214 hx.truncate(m * in_f);
215 for row in hx.chunks_mut(in_f) {
216 let amax = row.iter().fold(0f32, |a, &v| a.max(v.abs()));
217 if amax > 0.0 {
218 let d = amax / 127.0;
219 for v in row.iter_mut() {
220 *v = (*v / d).round().clamp(-127.0, 127.0) * d;
221 }
222 }
223 }
224 let xq = self.htod(&hx)?;
225 self.qmatvec_gemm_f16_raw(w16, &xq, m, in_f, out_f)?
226 } else {
227 self.qmatvec_gemm_f16_raw(w16, x, m, in_f, out_f)?
228 };
229 if scale != 1.0 {
230 self.scale_inplace(&mut y, scale, m * out_f)?;
231 }
232 Ok(Some(y))
233 }
234
235 pub fn bf16_tc_gemm(
239 &self,
240 data: &CudaSlice<u8>,
241 x: &CudaSlice<f32>,
242 m: usize,
243 in_f: usize,
244 out_f: usize,
245 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
246 let need_xh = m * in_f * 2;
247 let mut guard = self.f16_scratch.lock().unwrap();
248 if guard.is_none() {
249 *guard = Some(F16Scratch {
250 xh: self.alloc_u8_uninit(need_xh)?,
251 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
252 cap_xh: need_xh,
253 });
254 }
255 let s = guard.as_mut().unwrap();
256 if need_xh > s.cap_xh {
257 s.xh = self.alloc_u8_uninit(need_xh)?;
258 s.cap_xh = need_xh;
259 }
260 let mut y = self.uninit(m * out_f)?; let rc = {
262 let stream = self.gpu.stream();
263 let (w_p, _gw) = data.device_ptr(&stream);
264 let (x_p, _gx) = x.device_ptr(&stream);
265 let (h_p, _gh) = s.xh.device_ptr_mut(&stream);
266 let (y_p, _gy) = y.device_ptr_mut(&stream);
267 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
268 if (w_p as usize) % 16 != 0 {
272 -1
273 } else {
274 unsafe {
275 memra_bf16_pp_gemm(
276 w_p as *const core::ffi::c_void,
277 x_p as *const f32,
278 h_p as *mut core::ffi::c_void,
279 y_p as *mut f32,
280 m as i32,
281 out_f as i32,
282 in_f as i32,
283 ws_p as *mut core::ffi::c_void,
284 F16_WS_BYTES,
285 stream.cu_stream() as *mut core::ffi::c_void,
286 )
287 }
288 }
289 };
290 if rc != 0 {
297 static SAID: std::sync::Mutex<
298 Option<std::collections::HashSet<(usize, usize, usize)>>,
299 > = std::sync::Mutex::new(None);
300 let mut g = SAID.lock().unwrap();
301 let seen = g.get_or_insert_with(std::collections::HashSet::new);
302 if seen.insert((m, out_f, in_f)) {
303 eprintln!(
304 "[bf16-tc] DECLINED m={m} n={out_f} k={in_f} rc={rc} \
305 (1xxxx=cudaError convert, 2xxxx=no cublasLt algo, 3xxxx=matmul status, \
306 4xxxx=cublasLtCreate, -1=weight not 16B-aligned) — this shape falls back to \
307 the f32 dequant GEMM; every other shape keeps the tensor-core path"
308 );
309 }
310 return Ok(None);
311 }
312 {
318 static ACCEPTED: std::sync::Mutex<
319 Option<std::collections::HashSet<(usize, usize, usize)>>,
320 > = std::sync::Mutex::new(None);
321 let mut g = ACCEPTED.lock().unwrap();
322 let seen = g.get_or_insert_with(std::collections::HashSet::new);
323 if seen.insert((m, out_f, in_f)) {
324 eprintln!(
325 "[bf16-tc] ENGAGED m={m} n={out_f} k={in_f} (bf16 tensor-core GEMM on resident checkpoint bytes)"
326 );
327 }
328 }
329 Ok(Some(y))
330 }
331
332 pub fn qmatvec_gemm_f16_raw(
334 &self,
335 w16: &CudaSlice<u8>,
336 x: &CudaSlice<f32>,
337 m: usize,
338 in_f: usize,
339 out_f: usize,
340 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
341 let need_xh = m * in_f * 2;
342 let mut guard = self.f16_scratch.lock().unwrap();
343 if guard.is_none() {
344 *guard = Some(F16Scratch {
345 xh: self.alloc_u8_uninit(need_xh)?,
346 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
347 cap_xh: need_xh,
348 });
349 }
350 let s = guard.as_mut().unwrap();
351 if need_xh > s.cap_xh {
352 s.xh = self.alloc_u8_uninit(need_xh)?;
353 s.cap_xh = need_xh;
354 }
355 let mut y = self.uninit(m * out_f)?; let rc = {
357 let stream = self.gpu.stream();
358 let (w_p, _gw) = w16.device_ptr(&stream);
359 let (x_p, _gx) = x.device_ptr(&stream);
360 let (h_p, _gh) = s.xh.device_ptr_mut(&stream);
361 let (y_p, _gy) = y.device_ptr_mut(&stream);
362 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
363 unsafe {
364 memra_f16_pp_gemm(
365 w_p as *const core::ffi::c_void,
366 x_p as *const f32,
367 h_p as *mut core::ffi::c_void,
368 y_p as *mut f32,
369 m as i32,
370 out_f as i32,
371 in_f as i32,
372 ws_p as *mut core::ffi::c_void,
373 F16_WS_BYTES,
374 stream.cu_stream() as *mut core::ffi::c_void,
375 )
376 }
377 };
378 if rc != 0 {
379 return Err(format!(
380 "memra_f16_pp_gemm rc={rc} (m={m} n={out_f} k={in_f}; 1xxxx=cudaError convert, \
381 2xxxx=no cublasLt algo, 3xxxx=matmul status)"
382 )
383 .into());
384 }
385 Ok(y)
386 }
387
388 pub fn f16_act(
392 &self,
393 x: &CudaSlice<f32>,
394 nelem: usize,
395 in_f: usize,
396 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
397 static SIM_ACT2: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
401 if *SIM_ACT2.get_or_init(|| std::env::var("MEMRA_W8A8_SIM").as_deref() == Ok("2"))
402 && in_f > 0
403 && nelem % in_f == 0
404 {
405 static ONCE: std::sync::Once = std::sync::Once::new();
406 ONCE.call_once(|| {
407 eprintln!("[w8a8-sim] act per-token int8 fake-quant ACTIVE (f16_act)")
408 });
409 let mut hx = self.dtoh(x)?;
410 hx.truncate(nelem);
411 for row in hx.chunks_mut(in_f) {
412 let amax = row.iter().fold(0f32, |a, &v| a.max(v.abs()));
413 if amax > 0.0 {
414 let d = amax / 127.0;
415 for v in row.iter_mut() {
416 *v = (*v / d).round().clamp(-127.0, 127.0) * d;
417 }
418 }
419 }
420 let xq = self.htod(&hx)?;
421 let mut xh = self.alloc_u8_uninit(nelem * 2)?;
422 let rc = {
423 let stream = self.gpu.stream();
424 let (x_p, _gx) = xq.device_ptr(&stream);
425 let (h_p, _gh) = xh.device_ptr_mut(&stream);
426 unsafe {
427 memra_f16_cvt(
428 x_p as *const f32,
429 h_p as *mut core::ffi::c_void,
430 nelem,
431 stream.cu_stream() as *mut core::ffi::c_void,
432 )
433 }
434 };
435 if rc != 0 {
436 return Err(format!("memra_f16_cvt rc={rc}").into());
437 }
438 return Ok(xh);
439 }
440 let mut xh = self.alloc_u8_uninit(nelem * 2)?;
441 let rc = {
442 let stream = self.gpu.stream();
443 let (x_p, _gx) = x.device_ptr(&stream);
444 let (h_p, _gh) = xh.device_ptr_mut(&stream);
445 unsafe {
446 memra_f16_cvt(
447 x_p as *const f32,
448 h_p as *mut core::ffi::c_void,
449 nelem,
450 stream.cu_stream() as *mut core::ffi::c_void,
451 )
452 }
453 };
454 if rc != 0 {
455 return Err(format!("memra_f16_cvt rc={rc}").into());
456 }
457 Ok(xh)
458 }
459
460 pub fn try_f16_gemm_pre_into(
465 &self,
466 w: &crate::model::GpuTensor,
467 xh: &CudaSlice<u8>,
468 m: usize,
469 y: &mut CudaSlice<f32>,
470 ) -> Result<bool, Box<dyn std::error::Error>> {
471 use crate::model::GpuTensor;
472 let (w16, ne, scale) = match w {
473 GpuTensor::Quant {
474 f16: Some(w16),
475 ne,
476 scale,
477 ..
478 } => (w16, ne, *scale),
479 _ => return Ok(false),
480 };
481 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
482 assert!(
483 y.len() >= m * out_f,
484 "try_f16_gemm_pre_into: output slab too small"
485 );
486 let mut guard = self.f16_scratch.lock().unwrap();
487 if guard.is_none() {
488 *guard = Some(F16Scratch {
489 xh: self.alloc_u8_uninit(2)?,
490 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
491 cap_xh: 2,
492 });
493 }
494 let s = guard.as_mut().unwrap();
495 let rc = {
496 let stream = self.gpu.stream();
497 let (w_p, _gw) = w16.device_ptr(&stream);
498 let (h_p, _gh) = xh.device_ptr(&stream);
499 let (y_p, _gy) = y.device_ptr_mut(&stream);
500 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
501 unsafe {
502 memra_f16_pp_gemm_pre(
503 w_p as *const core::ffi::c_void,
504 h_p as *const core::ffi::c_void,
505 y_p as *mut f32,
506 m as i32,
507 out_f as i32,
508 in_f as i32,
509 ws_p as *mut core::ffi::c_void,
510 F16_WS_BYTES,
511 stream.cu_stream() as *mut core::ffi::c_void,
512 )
513 }
514 };
515 if rc != 0 {
516 return Err(
517 format!("memra_f16_pp_gemm_pre(into) rc={rc} (m={m} n={out_f} k={in_f})").into(),
518 );
519 }
520 if scale != 1.0 {
521 self.scale_inplace(y, scale, m * out_f)?;
522 }
523 Ok(true)
524 }
525
526 pub fn try_f16_gemm_pre_into_off(
530 &self,
531 w: &crate::model::GpuTensor,
532 xh: &CudaSlice<u8>,
533 m: usize,
534 y: &mut CudaSlice<f32>,
535 off_elems: usize,
536 ) -> Result<bool, Box<dyn std::error::Error>> {
537 use crate::model::GpuTensor;
538 let (w16, ne, scale) = match w {
539 GpuTensor::Quant {
540 f16: Some(w16),
541 ne,
542 scale,
543 ..
544 } => (w16, ne, *scale),
545 _ => return Ok(false),
546 };
547 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
548 assert!(
549 y.len() >= off_elems + m * out_f,
550 "try_f16_gemm_pre_into_off: output slab too small"
551 );
552 if scale != 1.0 {
553 return Ok(false); }
555 let mut guard = self.f16_scratch.lock().unwrap();
556 if guard.is_none() {
557 *guard = Some(F16Scratch {
558 xh: self.alloc_u8_uninit(2)?,
559 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
560 cap_xh: 2,
561 });
562 }
563 let s = guard.as_mut().unwrap();
564 let rc = {
565 let stream = self.gpu.stream();
566 let (w_p, _gw) = w16.device_ptr(&stream);
567 let (h_p, _gh) = xh.device_ptr(&stream);
568 let (y_p, _gy) = y.device_ptr_mut(&stream);
569 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
570 unsafe {
571 memra_f16_pp_gemm_pre(
572 w_p as *const core::ffi::c_void,
573 h_p as *const core::ffi::c_void,
574 (y_p as *mut f32).add(off_elems),
575 m as i32,
576 out_f as i32,
577 in_f as i32,
578 ws_p as *mut core::ffi::c_void,
579 F16_WS_BYTES,
580 stream.cu_stream() as *mut core::ffi::c_void,
581 )
582 }
583 };
584 if rc != 0 {
585 return Err(format!(
586 "memra_f16_pp_gemm_pre(into_off) rc={rc} (m={m} n={out_f} k={in_f})"
587 )
588 .into());
589 }
590 Ok(true)
591 }
592
593 pub fn try_f16_gemm_pre(
596 &self,
597 w: &crate::model::GpuTensor,
598 xh: &CudaSlice<u8>,
599 m: usize,
600 ) -> Result<Option<CudaSlice<f32>>, Box<dyn std::error::Error>> {
601 use crate::model::GpuTensor;
602 let (w16, ne, scale) = match w {
603 GpuTensor::Quant {
604 f16: Some(w16),
605 ne,
606 scale,
607 ..
608 } => (w16, ne, *scale),
609 _ => return Ok(None),
610 };
611 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
612 let mut guard = self.f16_scratch.lock().unwrap();
614 if guard.is_none() {
615 *guard = Some(F16Scratch {
616 xh: self.alloc_u8_uninit(2)?,
617 ws: self.alloc_u8_uninit(F16_WS_BYTES)?,
618 cap_xh: 2,
619 });
620 }
621 let s = guard.as_mut().unwrap();
622 let mut y = self.uninit(m * out_f)?;
623 let rc = {
624 let stream = self.gpu.stream();
625 let (w_p, _gw) = w16.device_ptr(&stream);
626 let (h_p, _gh) = xh.device_ptr(&stream);
627 let (y_p, _gy) = y.device_ptr_mut(&stream);
628 let (ws_p, _gws) = s.ws.device_ptr_mut(&stream);
629 unsafe {
630 memra_f16_pp_gemm_pre(
631 w_p as *const core::ffi::c_void,
632 h_p as *const core::ffi::c_void,
633 y_p as *mut f32,
634 m as i32,
635 out_f as i32,
636 in_f as i32,
637 ws_p as *mut core::ffi::c_void,
638 F16_WS_BYTES,
639 stream.cu_stream() as *mut core::ffi::c_void,
640 )
641 }
642 };
643 if rc != 0 {
644 return Err(format!("memra_f16_pp_gemm_pre rc={rc} (m={m} n={out_f} k={in_f})").into());
645 }
646 if scale != 1.0 {
647 self.scale_inplace(&mut y, scale, m * out_f)?;
648 }
649 Ok(Some(y))
650 }
651
652 pub fn build_q8_f16_raw(
655 &self,
656 bytes: &CudaSlice<u8>,
657 in_f: usize,
658 out_f: usize,
659 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
660 assert!(in_f % 32 == 0);
661 let nblk = in_f / 32;
662 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
663 let rc = {
664 let stream = self.gpu.stream();
665 let (s_p, _gs) = bytes.device_ptr(&stream);
666 let (d_p, _gd) = dst.device_ptr_mut(&stream);
667 unsafe {
668 memra_q8_0_dequant_f16(
669 s_p as *const core::ffi::c_void,
670 d_p as *mut core::ffi::c_void,
671 out_f as i64,
672 nblk as i64,
673 stream.cu_stream() as *mut core::ffi::c_void,
674 )
675 }
676 };
677 if rc != 0 {
678 return Err(format!("memra_q8_0_dequant_f16 rc={rc}").into());
679 }
680 Ok(dst)
681 }
682
683 pub fn build_q8_f16(
686 &self,
687 t: &mut crate::model::GpuTensor,
688 ) -> Result<(), Box<dyn std::error::Error>> {
689 use crate::model::GpuTensor;
690 let GpuTensor::Quant {
691 bytes,
692 qtype,
693 row_bytes,
694 ne,
695 f16,
696 ..
697 } = t
698 else {
699 return Ok(());
700 };
701 let q4 = *qtype == crate::QT_Q4_0;
709 let q6k = *qtype == crate::QT_Q6_K;
710 let q4k = *qtype == crate::QT_Q4_K;
711 let q5k = *qtype == crate::QT_Q5_K;
712 if (*qtype != crate::QT_Q8_0 && !q4 && !q6k && !q4k && !q5k)
713 || f16.is_some()
714 || ne.len() != 2
715 {
716 return Ok(());
717 }
718 let (in_f, out_f) = (ne[0] as usize, ne[1] as usize);
719 if q6k || q4k || q5k {
720 let sb = if q6k {
721 210
722 } else if q5k {
723 176
724 } else {
725 144
726 };
727 if in_f % 256 != 0 || *row_bytes != (in_f / 256) * sb {
728 return Ok(());
729 }
730 } else if in_f % 32 != 0 || *row_bytes != (in_f / 32) * (if q4 { 18 } else { 34 }) {
731 return Ok(());
732 }
733 use std::sync::atomic::{AtomicUsize, Ordering};
736 static SPENT: AtomicUsize = AtomicUsize::new(0);
737 static BUDGET: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
738 let budget = *BUDGET.get_or_init(|| {
739 std::env::var("MEMRA_PP_F16_BUDGET_MB")
740 .ok()
741 .and_then(|v| v.parse::<usize>().ok())
742 .unwrap_or(32768)
743 << 20
744 });
745 let sz = out_f * in_f * 2;
746 if SPENT.fetch_add(sz, Ordering::Relaxed) + sz > budget {
747 SPENT.fetch_sub(sz, Ordering::Relaxed);
748 return Ok(());
749 }
750 let mut mirror = if q6k {
751 self.build_q6k_f16_raw(bytes, in_f, out_f)?
752 } else if q4k {
753 self.build_q4k_f16_raw(bytes, in_f, out_f)?
754 } else if q5k {
755 self.build_q5k_f16_raw(bytes, in_f, out_f)?
756 } else if q4 {
757 self.build_q4_f16_raw(bytes, in_f, out_f)?
758 } else {
759 self.build_q8_f16_raw(bytes, in_f, out_f)?
760 };
761 static SIM: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
770 if *SIM.get_or_init(|| {
771 matches!(
772 std::env::var("MEMRA_W8A8_SIM").as_deref(),
773 Ok("1") | Ok("2")
774 )
775 }) {
776 fn f16_bits_to_f32(b: u16) -> f32 {
777 let (s, e, m) = (
778 (b >> 15) as u32,
779 ((b >> 10) & 0x1f) as u32,
780 (b & 0x3ff) as u32,
781 );
782 let bits = if e == 0 {
783 if m == 0 {
784 s << 31
785 } else {
786 let mut e2 = 127 - 15 + 1;
788 let mut m2 = m;
789 while m2 & 0x400 == 0 {
790 m2 <<= 1;
791 e2 -= 1;
792 }
793 (s << 31) | ((e2 as u32) << 23) | ((m2 & 0x3ff) << 13)
794 }
795 } else if e == 0x1f {
796 (s << 31) | (0xff << 23) | (m << 13)
797 } else {
798 (s << 31) | ((e + 127 - 15) << 23) | (m << 13)
799 };
800 f32::from_bits(bits)
801 }
802 fn f32_to_f16_bits(v: f32) -> u16 {
803 let b = v.to_bits();
804 let (s, e, m) = ((b >> 31) as u16, ((b >> 23) & 0xff) as i32, b & 0x7fffff);
805 if e == 0xff {
806 return (s << 15) | 0x7c00 | ((m >> 13) as u16 & 0x3ff);
807 }
808 let e2 = e - 127 + 15;
809 if e2 >= 0x1f {
810 return (s << 15) | 0x7c00;
811 }
812 if e2 <= 0 {
813 if e2 < -10 {
814 return s << 15;
815 }
816 let m2 = (m | 0x800000) >> (1 - e2);
817 let r = (m2 >> 13) as u16 + ((m2 >> 12) & 1) as u16;
819 return (s << 15) | r;
820 }
821 let mut r = ((e2 as u32) << 10) as u16 | (m >> 13) as u16;
822 if m & 0x1000 != 0 {
823 r += 1;
824 }
825 (s << 15) | r
826 }
827 let host: Vec<u8> = self.dtoh_u8(&mirror)?;
828 let mut vals: Vec<f32> = host
829 .chunks_exact(2)
830 .map(|c| f16_bits_to_f32(u16::from_le_bytes([c[0], c[1]])))
831 .collect();
832 for row in vals.chunks_mut(in_f) {
833 let amax = row.iter().fold(0f32, |a, &v| a.max(v.abs()));
834 if amax > 0.0 {
835 let d = amax / 127.0;
836 for v in row.iter_mut() {
837 *v = (*v / d).round().clamp(-127.0, 127.0) * d;
838 }
839 }
840 }
841 let out: Vec<u8> = vals
842 .iter()
843 .flat_map(|&v| f32_to_f16_bits(v).to_le_bytes())
844 .collect();
845 mirror = self.htod_bytes(&out)?;
846 }
847 *f16 = Some(mirror);
848 Ok(())
849 }
850
851 pub fn build_q4_f16_raw(
853 &self,
854 bytes: &CudaSlice<u8>,
855 in_f: usize,
856 out_f: usize,
857 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
858 assert!(in_f % 32 == 0);
859 let nblk = in_f / 32;
860 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
861 let rc = {
862 let stream = self.gpu.stream();
863 let (s_p, _gs) = bytes.device_ptr(&stream);
864 let (d_p, _gd) = dst.device_ptr_mut(&stream);
865 unsafe {
866 memra_q4_0_dequant_f16(
867 s_p as *const core::ffi::c_void,
868 d_p as *mut core::ffi::c_void,
869 out_f as i64,
870 nblk as i64,
871 stream.cu_stream() as *mut core::ffi::c_void,
872 )
873 }
874 };
875 if rc != 0 {
876 return Err(format!("memra_q4_0_dequant_f16 rc={rc}").into());
877 }
878 Ok(dst)
879 }
880
881 pub fn build_q5k_f16_raw(
884 &self,
885 bytes: &CudaSlice<u8>,
886 in_f: usize,
887 out_f: usize,
888 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
889 assert!(in_f % 256 == 0);
890 let nsb = in_f / 256;
891 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
892 let rc = {
893 let stream = self.gpu.stream();
894 let (s_p, _gs) = bytes.device_ptr(&stream);
895 let (d_p, _gd) = dst.device_ptr_mut(&stream);
896 unsafe {
897 memra_q5_K_dequant_f16(
898 s_p as *const core::ffi::c_void,
899 d_p as *mut core::ffi::c_void,
900 out_f as i64,
901 nsb as i64,
902 stream.cu_stream() as *mut core::ffi::c_void,
903 )
904 }
905 };
906 if rc != 0 {
907 return Err(format!("memra_q5_K_dequant_f16 rc={rc}").into());
908 }
909 Ok(dst)
910 }
911
912 pub fn build_q4k_f16_raw(
915 &self,
916 bytes: &CudaSlice<u8>,
917 in_f: usize,
918 out_f: usize,
919 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
920 assert!(in_f % 256 == 0);
921 let nsb = in_f / 256;
922 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
923 let rc = {
924 let stream = self.gpu.stream();
925 let (s_p, _gs) = bytes.device_ptr(&stream);
926 let (d_p, _gd) = dst.device_ptr_mut(&stream);
927 unsafe {
928 memra_q4_K_dequant_f16(
929 s_p as *const core::ffi::c_void,
930 d_p as *mut core::ffi::c_void,
931 out_f as i64,
932 nsb as i64,
933 stream.cu_stream() as *mut core::ffi::c_void,
934 )
935 }
936 };
937 if rc != 0 {
938 return Err(format!("memra_q4_K_dequant_f16 rc={rc}").into());
939 }
940 Ok(dst)
941 }
942
943 pub fn build_q6k_f16_raw(
944 &self,
945 bytes: &CudaSlice<u8>,
946 in_f: usize,
947 out_f: usize,
948 ) -> Result<CudaSlice<u8>, Box<dyn std::error::Error>> {
949 assert!(in_f % 256 == 0);
950 let nsb = in_f / 256;
951 let mut dst = self.alloc_u8_uninit(out_f * in_f * 2)?;
952 let rc = {
953 let stream = self.gpu.stream();
954 let (s_p, _gs) = bytes.device_ptr(&stream);
955 let (d_p, _gd) = dst.device_ptr_mut(&stream);
956 unsafe {
957 memra_q6_K_dequant_f16(
958 s_p as *const core::ffi::c_void,
959 d_p as *mut core::ffi::c_void,
960 out_f as i64,
961 nsb as i64,
962 stream.cu_stream() as *mut core::ffi::c_void,
963 )
964 }
965 };
966 if rc != 0 {
967 return Err(format!("memra_q6_K_dequant_f16 rc={rc}").into());
968 }
969 Ok(dst)
970 }
971}