metrale_model_layers/weight_map/
fp8_dequant.rs1#![allow(unused_imports)]
11
12use anyhow::{Context, Result, bail, ensure};
13use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
14use metrale_model_weights::weights::{WeightDtype, WeightStore};
15
16use super::*;
17
18fn gpu_dequant_fp8_pertensor(
27 store: &WeightStore,
28 prefix: &str,
29 gpu: &dyn GpuBackend,
30 out: DevicePtr,
31) -> Result<()> {
32 use metrale_gpu_runtime::kernel_args::{KernelLaunch, div_ceil};
33
34 let w = store.get(&format!("{prefix}.weight"))?;
35 ensure!(
36 w.dtype == WeightDtype::FP8E4M3,
37 "Expected FP8E4M3 for {prefix}.weight, got {:?}",
38 w.dtype,
39 );
40 let total = w.num_elements();
43 let (n, k) = if w.shape.len() == 2 {
44 (w.shape[0], w.shape[1])
45 } else {
46 (1usize, total)
47 };
48 ensure!(
49 n * k == total,
50 "FP8 shape mismatch for {prefix}: {n}*{k} != {total}"
51 );
52
53 let s = store.get(&format!("{prefix}.weight_scale"))?;
54 ensure!(
55 s.dtype == WeightDtype::FP32 && s.num_elements() == 1,
56 "Expected FP32 scalar weight_scale for {prefix}, got {:?} ({} elems)",
57 s.dtype,
58 s.num_elements(),
59 );
60
61 let stream = gpu.default_stream();
62 let kernel = gpu.kernel(
63 "dequant_fp8_blockscaled_bf16",
64 "dequant_fp8_blockscaled_bf16",
65 )?;
66 KernelLaunch::new(gpu, kernel)
67 .grid([div_ceil(k as u32, 64), div_ceil(n as u32, 4), 1])
68 .block([64, 4, 1])
69 .arg_ptr(w.ptr)
70 .arg_ptr(s.ptr)
71 .arg_ptr(out)
72 .arg_u32(n as u32)
73 .arg_u32(k as u32)
74 .arg_u32(n as u32)
75 .arg_u32(k as u32)
76 .arg_u32(1)
77 .arg_u32(1)
78 .launch(stream)?;
79 Ok(())
82}
83
84fn read_scalar_weight_scale(
87 store: &WeightStore,
88 prefix: &str,
89 gpu: &dyn GpuBackend,
90) -> Result<f32> {
91 let scale_key = format!("{prefix}.weight_scale");
92 let s = store.get(&scale_key)?;
93 ensure!(
94 s.num_elements() == 1,
95 "Expected scalar for {scale_key}, got {} elements",
96 s.num_elements()
97 );
98 match s.dtype {
99 WeightDtype::FP32 => {
100 let mut buf = [0u8; 4];
101 gpu.copy_d2h(s.ptr, &mut buf)?;
102 Ok(f32::from_le_bytes(buf))
103 }
104 WeightDtype::BF16 => {
105 let mut buf = [0u8; 2];
106 gpu.copy_d2h(s.ptr, &mut buf)?;
107 Ok(bf16_bytes_to_f32(buf))
108 }
109 other => bail!("Expected FP32 or BF16 for {scale_key}, got {:?}", other),
110 }
111}
112
113pub(crate) fn dequant_fp8_to_bf16(
117 store: &WeightStore,
118 prefix: &str,
119 gpu: &dyn GpuBackend,
120) -> Result<DenseWeight> {
121 let w = store.get(&format!("{prefix}.weight"))?;
122 let scale_is_fp32_scalar = store
125 .get(&format!("{prefix}.weight_scale"))
126 .map(|s| s.dtype == WeightDtype::FP32 && s.num_elements() == 1)
127 .unwrap_or(false);
128 if scale_is_fp32_scalar {
129 let total = w.num_elements();
130 let out = gpu.alloc(total * 2)?;
131 gpu_dequant_fp8_pertensor(store, prefix, gpu, out)?;
132 Ok(DenseWeight { weight: out })
133 } else {
134 let n_bytes = w.num_elements();
135 let mut fp8_buf = vec![0u8; n_bytes];
136 gpu.copy_d2h(w.ptr, &mut fp8_buf)?;
137
138 let scale = read_scalar_weight_scale(store, prefix, gpu)?;
139 let bf16_buf = dequant_fp8_bytes_to_bf16(&fp8_buf, scale);
140 let ptr = gpu.alloc(bf16_buf.len())?;
141 gpu.copy_h2d(&bf16_buf, ptr)?;
142 Ok(DenseWeight { weight: ptr })
143 }
144}
145
146pub fn dequant_fp8_to_bf16_into(
152 store: &WeightStore,
153 prefix: &str,
154 gpu: &dyn GpuBackend,
155 dest: DevicePtr,
156) -> Result<DenseWeight> {
157 let scale_is_fp32_scalar = store
159 .get(&format!("{prefix}.weight_scale"))
160 .map(|s| s.dtype == WeightDtype::FP32 && s.num_elements() == 1)
161 .unwrap_or(false);
162 if scale_is_fp32_scalar {
163 gpu_dequant_fp8_pertensor(store, prefix, gpu, dest)?;
164 } else {
165 let w = store.get(&format!("{prefix}.weight"))?;
166 let n_bytes = w.num_elements();
167 let mut fp8_buf = vec![0u8; n_bytes];
168 gpu.copy_d2h(w.ptr, &mut fp8_buf)?;
169
170 let scale = read_scalar_weight_scale(store, prefix, gpu)?;
171 let bf16_buf = dequant_fp8_bytes_to_bf16(&fp8_buf, scale);
172 gpu.copy_h2d(&bf16_buf, dest)?;
173 }
174 Ok(DenseWeight { weight: dest })
175}