metrale_model_arch/kimi_k3/
dense_cuda.rs1use anyhow::{Context, Result, ensure};
15use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
16use metrale_gpu_runtime::kernel_args::{KernelLaunch, div_ceil};
17use metrale_model_weights::weights::WeightDtype;
18
19use super::bound::K3BoundLayer;
20
21fn dtype_tag(dtype: WeightDtype) -> Result<u32> {
22 match dtype {
23 WeightDtype::FP32 => Ok(0),
24 WeightDtype::BF16 => Ok(1),
25 other => anyhow::bail!("K3 dense GPU: unsupported resident weight dtype {other:?}"),
26 }
27}
28
29struct Scratch<'a> {
30 gpu: &'a dyn GpuBackend,
31 pointers: Vec<DevicePtr>,
32}
33impl Scratch<'_> {
34 fn alloc(&mut self, floats: usize) -> Result<DevicePtr> {
35 let bytes = floats
36 .checked_mul(4)
37 .context("K3 dense scratch size overflow")?;
38 let p = self.gpu.alloc(bytes)?;
39 self.pointers.push(p);
40 Ok(p)
41 }
42}
43impl Drop for Scratch<'_> {
44 fn drop(&mut self) {
45 for p in self.pointers.drain(..).rev() {
46 let _ = self.gpu.free(p);
47 }
48 }
49}
50
51#[allow(clippy::too_many_arguments)]
56pub fn launch_dense_mlp(
57 layer: &K3BoundLayer,
58 gpu: &dyn GpuBackend,
59 x: &[f32],
60 hidden: usize,
61 inter: usize,
62 beta: f32,
63 linear_beta: f32,
64 stream: u64,
65) -> Result<Vec<f32>> {
66 ensure!(
67 hidden > 0 && inter > 0 && x.len() == hidden,
68 "K3 dense GPU: invalid activation geometry"
69 );
70 ensure!(
71 beta.is_finite() && beta > 0.0 && linear_beta.is_finite() && linear_beta > 0.0,
72 "K3 dense GPU: invalid SiTU coefficients"
73 );
74 let elements = hidden
75 .checked_mul(inter)
76 .context("K3 dense weight size overflow")?;
77 ensure!(
78 elements <= u32::MAX as usize,
79 "K3 dense GPU: matrix exceeds 32-bit kernel indexing"
80 );
81 let prefix = match layer.spec.mlp {
82 metrale_model_weights::kimi_k3_host::MlpKind::Dense => ".mlp.",
83 metrale_model_weights::kimi_k3_host::MlpKind::LatentMoe => {
84 ".block_sparse_moe.shared_experts."
85 }
86 };
87 let find = |role: &str| -> Result<_> {
88 let suffix = format!("{prefix}{role}_proj.weight");
89 let (weight, meta) = layer
90 .weights
91 .iter()
92 .zip(&layer.weight_meta)
93 .find(|(_, meta)| meta.name.ends_with(&suffix))
94 .with_context(|| format!("K3 dense GPU: resident {suffix} missing"))?;
95 ensure!(
96 meta.numel == elements,
97 "K3 dense GPU: {} has {} values, expected {elements}",
98 meta.name,
99 meta.numel
100 );
101 Ok((weight.weight, dtype_tag(meta.dtype)?))
102 };
103 let (gate, gate_dtype) = find("gate")?;
104 let (up, up_dtype) = find("up")?;
105 let (down, down_dtype) = find("down")?;
106 ensure!(
107 gate_dtype == up_dtype,
108 "K3 dense GPU: gate/up dtypes differ"
109 );
110 launch_dense_matrices(
111 gpu,
112 x,
113 gate,
114 up,
115 down,
116 gate_dtype,
117 down_dtype,
118 hidden,
119 inter,
120 beta,
121 linear_beta,
122 stream,
123 )
124}
125
126#[allow(clippy::too_many_arguments)]
129pub fn launch_dense_matrices(
130 gpu: &dyn GpuBackend,
131 x: &[f32],
132 gate: DevicePtr,
133 up: DevicePtr,
134 down: DevicePtr,
135 gate_dtype: u32,
136 down_dtype: u32,
137 hidden: usize,
138 inter: usize,
139 beta: f32,
140 linear_beta: f32,
141 stream: u64,
142) -> Result<Vec<f32>> {
143 ensure!(
144 hidden > 0 && inter > 0 && x.len() == hidden,
145 "K3 dense GPU: invalid dimensions"
146 );
147 ensure!(
148 hidden
149 .checked_mul(inter)
150 .is_some_and(|n| n <= u32::MAX as usize),
151 "K3 dense GPU: index overflow"
152 );
153 ensure!(
154 gate_dtype <= 1 && down_dtype <= 1,
155 "K3 dense GPU: invalid dtype tag"
156 );
157 ensure!(
158 beta.is_finite() && beta > 0.0 && linear_beta.is_finite() && linear_beta > 0.0,
159 "K3 dense GPU: invalid activation coefficients"
160 );
161 let gate_kernel = gpu.kernel("dense_f32io", "k3_dense_gate_up_situ_f32io")?;
162 let down_kernel = gpu.kernel("dense_f32io", "k3_dense_down_f32io")?;
163 let mut scratch = Scratch {
164 gpu,
165 pointers: Vec::new(),
166 };
167 let input = scratch.alloc(hidden)?;
168 let mid = scratch.alloc(inter)?;
169 let output = scratch.alloc(hidden)?;
170 gpu.copy_h2d(
171 &x.iter().flat_map(|v| v.to_le_bytes()).collect::<Vec<_>>(),
172 input,
173 )?;
174 KernelLaunch::new(gpu, gate_kernel)
175 .grid([div_ceil(inter as u32, 4), 1, 1])
176 .block([128, 1, 1])
177 .arg_ptr(input)
178 .arg_ptr(gate)
179 .arg_ptr(up)
180 .arg_ptr(mid)
181 .arg_u32(inter as u32)
182 .arg_u32(hidden as u32)
183 .arg_u32(gate_dtype)
184 .arg_f32(beta)
185 .arg_f32(linear_beta)
186 .launch(stream)?;
187 KernelLaunch::new(gpu, down_kernel)
188 .grid([div_ceil(hidden as u32, 4), 1, 1])
189 .block([128, 1, 1])
190 .arg_ptr(mid)
191 .arg_ptr(down)
192 .arg_ptr(output)
193 .arg_u32(hidden as u32)
194 .arg_u32(inter as u32)
195 .arg_u32(down_dtype)
196 .launch(stream)?;
197 gpu.synchronize(stream)?;
198 let mut raw = vec![0; hidden * 4];
199 gpu.copy_d2h(output, &mut raw)?;
200 let result: Vec<f32> = raw
201 .chunks_exact(4)
202 .map(|b| f32::from_le_bytes(b.try_into().unwrap()))
203 .collect();
204 ensure!(
205 result.iter().all(|v| v.is_finite()),
206 "K3 dense GPU: nonfinite output"
207 );
208 Ok(result)
209}