metrale_model_layers/layers/ops/
nvfp4_moe_grouped.rs1use anyhow::Result;
16use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend, KernelHandle};
17use metrale_gpu_runtime::kernel_args::{KernelLaunch, div_ceil};
18
19use super::Fp8GroupedGeometry;
20use crate::weight_map::QuantizedWeight;
21
22pub const NVFP4_GROUPED_GATE_UP_COLS_PER_CTA: u32 = 16;
24
25pub const NVFP4_GROUPED_GATE_UP_ROWS_PER_PASS: u32 = 4;
27
28pub const NVFP4_GROUPED_DOWN_COLS_PER_CTA: u32 = 128;
30
31pub const NVFP4_GROUPED_DOWN_ROWS_PER_PASS: u32 = 4;
33
34pub const NVFP4_GROUPED_GATE_UP_SCALAR: Fp8GroupedGeometry = Fp8GroupedGeometry {
36 cols_per_cta: NVFP4_GROUPED_GATE_UP_COLS_PER_CTA,
37 rows_per_pass: NVFP4_GROUPED_GATE_UP_ROWS_PER_PASS,
38 threads: 128,
39};
40
41pub const NVFP4_GROUPED_DOWN_SCALAR: Fp8GroupedGeometry = Fp8GroupedGeometry {
43 cols_per_cta: NVFP4_GROUPED_DOWN_COLS_PER_CTA,
44 rows_per_pass: NVFP4_GROUPED_DOWN_ROWS_PER_PASS,
45 threads: 256,
46};
47
48pub const NVFP4_GROUPED_TC_ROWS_PER_PASS: u32 = 8;
51
52pub const NVFP4_GROUPED_GATE_UP_TC: Fp8GroupedGeometry = Fp8GroupedGeometry {
55 cols_per_cta: 64,
56 rows_per_pass: NVFP4_GROUPED_TC_ROWS_PER_PASS,
57 threads: 128,
58};
59
60pub const NVFP4_GROUPED_DOWN_TC: Fp8GroupedGeometry = Fp8GroupedGeometry {
63 cols_per_cta: 128,
64 rows_per_pass: NVFP4_GROUPED_TC_ROWS_PER_PASS,
65 threads: 128,
66};
67
68pub fn nvfp4_grouped_tc_shape_ok(n: u32, k: u32, geometry: Fp8GroupedGeometry) -> bool {
71 n > 0 && k > 0 && k.is_multiple_of(256) && n.is_multiple_of(geometry.cols_per_cta)
72}
73
74#[derive(Clone, Copy, Debug)]
77pub struct Nvfp4ExpertTables {
78 pub packed_ptrs: DevicePtr,
79 pub scale_ptrs: DevicePtr,
80 pub scale2_vals: DevicePtr,
81}
82
83#[allow(clippy::too_many_arguments)]
90pub fn moe_expert_gate_up_act_nvfp4_grouped(
91 gpu: &dyn GpuBackend,
92 kernel: KernelHandle,
93 geometry: Fp8GroupedGeometry,
94 input: DevicePtr,
95 gate: Nvfp4ExpertTables,
96 up: Nvfp4ExpertTables,
97 act: DevicePtr,
98 expert_offsets: DevicePtr,
99 sorted_token_ids: DevicePtr,
100 active_experts: DevicePtr,
101 active_count: DevicePtr,
102 sh_gate: &QuantizedWeight,
103 sh_up: &QuantizedWeight,
104 sh_act: DevicePtr,
105 n: u32,
106 k: u32,
107 cap: u32,
108 num_tokens: u32,
109 stream: u64,
110) -> Result<()> {
111 KernelLaunch::new(gpu, kernel)
112 .grid([
113 div_ceil(n, geometry.cols_per_cta),
114 cap + div_ceil(num_tokens, geometry.rows_per_pass),
115 1,
116 ])
117 .block([geometry.threads, 1, 1])
118 .arg_ptr(input)
119 .arg_ptr(gate.packed_ptrs)
120 .arg_ptr(gate.scale_ptrs)
121 .arg_ptr(gate.scale2_vals)
122 .arg_ptr(up.packed_ptrs)
123 .arg_ptr(up.scale_ptrs)
124 .arg_ptr(up.scale2_vals)
125 .arg_ptr(act)
126 .arg_ptr(expert_offsets)
127 .arg_ptr(sorted_token_ids)
128 .arg_ptr(active_experts)
129 .arg_ptr(active_count)
130 .arg_ptr(sh_gate.weight)
131 .arg_ptr(sh_gate.weight_scale)
132 .arg_f32(sh_gate.weight_scale_2)
133 .arg_ptr(sh_up.weight)
134 .arg_ptr(sh_up.weight_scale)
135 .arg_f32(sh_up.weight_scale_2)
136 .arg_ptr(sh_act)
137 .arg_u32(n)
138 .arg_u32(k)
139 .arg_u32(cap)
140 .arg_u32(num_tokens)
141 .launch(stream)
142}
143
144#[allow(clippy::too_many_arguments)]
147pub fn nvfp4_tc_lean_repack(
154 gpu: &dyn GpuBackend,
155 kernel: KernelHandle,
156 packed: DevicePtr,
157 scale: DevicePtr,
158 n: u32,
159 k: u32,
160 bad: DevicePtr,
161 stream: u64,
162) -> Result<()> {
163 anyhow::ensure!(
164 nvfp4_lean_repack_shape_ok(n, k),
165 "nvfp4_tc_lean_repack: shape [{n}, {k}] not admitted"
166 );
167 KernelLaunch::new(gpu, kernel)
168 .grid([n / 16, 1, 1])
169 .block([256, 1, 1])
170 .shared_mem(NVFP4_LEAN_REPACK_SMEM_PER_K * k)
171 .arg_ptr(packed)
172 .arg_ptr(scale)
173 .arg_u32(k)
174 .arg_ptr(bad)
175 .launch(stream)
176}
177
178pub const NVFP4_LEAN_REPACK_SMEM_PER_K: u32 = 9;
181
182pub fn nvfp4_lean_repack_shape_ok(n: u32, k: u32) -> bool {
185 n > 0
186 && n.is_multiple_of(16)
187 && k > 0
188 && k.is_multiple_of(128)
189 && NVFP4_LEAN_REPACK_SMEM_PER_K * k <= 48 * 1024
190}
191
192pub fn moe_expert_down_act_nvfp4_grouped(
193 gpu: &dyn GpuBackend,
194 kernel: KernelHandle,
195 geometry: Fp8GroupedGeometry,
196 act: DevicePtr,
197 down: Nvfp4ExpertTables,
198 output: DevicePtr,
199 expert_offsets: DevicePtr,
200 active_experts: DevicePtr,
201 active_count: DevicePtr,
202 sh_act: DevicePtr,
203 sh_down: &QuantizedWeight,
204 sh_down_out: DevicePtr,
205 n: u32,
206 k: u32,
207 cap: u32,
208 num_tokens: u32,
209 stream: u64,
210) -> Result<()> {
211 KernelLaunch::new(gpu, kernel)
212 .grid([
213 div_ceil(n, geometry.cols_per_cta),
214 cap + div_ceil(num_tokens, geometry.rows_per_pass),
215 1,
216 ])
217 .block([geometry.threads, 1, 1])
218 .arg_ptr(act)
219 .arg_ptr(down.packed_ptrs)
220 .arg_ptr(down.scale_ptrs)
221 .arg_ptr(down.scale2_vals)
222 .arg_ptr(output)
223 .arg_ptr(expert_offsets)
224 .arg_ptr(active_experts)
225 .arg_ptr(active_count)
226 .arg_ptr(sh_act)
227 .arg_ptr(sh_down.weight)
228 .arg_ptr(sh_down.weight_scale)
229 .arg_f32(sh_down.weight_scale_2)
230 .arg_ptr(sh_down_out)
231 .arg_u32(n)
232 .arg_u32(k)
233 .arg_u32(cap)
234 .arg_u32(num_tokens)
235 .launch(stream)
236}
237
238#[cfg(test)]
239#[path = "nvfp4_moe_grouped_tests.rs"]
240mod tests;