1use super::*;
9
10impl MoeLayer {
11 pub fn forward_k3(
18 &self,
19 input: DevicePtr, ctx: &ForwardContext,
21 stream: u64,
22 ) -> Result<()> {
23 if self.nvfp4_grouped_decode_ok(3, ctx) {
26 return self.forward_nvfp4_grouped_decode(input, 3, ctx, stream);
27 }
28 self.refuse_lean_layout("MoeLayer::forward_k3")?;
29 anyhow::ensure!(
32 self.router_logits_n as usize == ctx.config.num_experts,
33 "zero-expert MoE routing is not wired on this dispatch variant yet (forward_k3)"
34 );
35
36 if self.lora.is_some() {
40 return self.forward_batched(input, 3, ctx, stream);
41 }
42 if self.bf16_gate_weight_ptrs.is_some() {
46 return self.forward_batched(input, 3, ctx, stream);
47 }
48 let mixed_bf16_shared = self.has_mixed_bf16_shared_expert();
52 if mixed_bf16_shared
53 && !(self.use_t_layout_for_decode()
54 && self.moe_expert_gate_up_shared_batch3_t_k.0 != 0
55 && self.moe_expert_silu_down_shared_batch3_t_k.0 != 0
56 && !(ctx.comm.is_some() && ctx.config.ep_world_size > 1))
57 {
58 return self.forward_batched(input, 3, ctx, stream);
59 }
60 if k3_e8m0_needs_per_token(self.experts_scale_kind) {
66 return self.forward_batched(input, 3, ctx, stream);
67 }
68
69 let h = ctx.config.hidden_size as u32;
70 let inter = ctx.config.moe_intermediate_size as u32;
71 let num_experts = ctx.config.num_experts as u32;
72 let top_k = ctx.config.num_experts_per_tok as u32;
73
74 let router_in = self.router_input(input, 3, h, ctx, stream)?;
75 let gate_logits = ctx.buffers.gate_logits();
76 if let Some(ref nvfp4) = self.gate_nvfp4 {
77 ops::w4a16_gemv_batch3(
78 ctx.gpu,
79 self.w4a16_gemv_batch3,
80 router_in,
81 nvfp4,
82 gate_logits,
83 num_experts,
84 h,
85 stream,
86 )?;
87 } else {
88 self.router_gemm_bf16(router_in, gate_logits, 3, num_experts, h, ctx, stream)?;
89 }
90
91 let scratch = ctx.buffers.scratch();
95 let indices_dev = scratch;
96 let weights_dev = scratch.offset(3 * top_k as usize * 4);
97 if let Some(bias) = self.correction_bias_dev {
98 ops::moe_topk_sigmoid_batched(
99 ctx.gpu,
100 self.moe_topk_sigmoid_batched_k,
101 gate_logits,
102 bias,
103 indices_dev,
104 weights_dev,
105 num_experts,
106 top_k,
107 ctx.config.norm_topk_prob,
108 ctx.config.routed_scaling_factor as f32,
109 3,
110 stream,
111 )?;
112 } else {
113 ops::moe_topk_softmax_batched(
114 ctx.gpu,
115 self.moe_topk_batched,
116 gate_logits,
117 indices_dev,
118 weights_dev,
119 num_experts,
120 top_k,
121 ctx.config.norm_topk_prob,
122 3,
123 stream,
124 )?;
125 }
126
127 super::union_stats::maybe_sample_expert_union(ctx, indices_dev, 3, top_k as usize, stream);
128
129 let expert_gate_out = ctx.buffers.expert_gate_out();
130 let expert_up_out = ctx.buffers.expert_up_out();
131 let shared_gate_scratch = ctx.buffers.logits();
132 let shared_up_scratch = ctx.buffers.ssm_qkvz();
133 let expert_down_out = ctx.buffers.expert_down_out();
134 let shared_down_out = ctx.buffers.attn_output();
135 let output = ctx.buffers.moe_output();
136
137 let is_ep = ctx.comm.is_some() && ctx.config.ep_world_size > 1;
138
139 if let (Some(gp), Some(up), Some(dp), Some(sh)) = (
140 &self.fp8_gate_weight_ptrs,
141 &self.fp8_up_weight_ptrs,
142 &self.fp8_down_weight_ptrs,
143 &self.fp8_shared_expert,
144 ) {
145 ops::moe_expert_gate_up_shared_fp8_batch3(
146 ctx.gpu,
147 self.moe_expert_gate_up_shared_fp8_batch3,
148 input,
149 gp.weight_ptrs,
150 gp.scale_ptrs,
151 expert_gate_out,
152 up.weight_ptrs,
153 up.scale_ptrs,
154 expert_up_out,
155 indices_dev,
156 &sh.gate_proj,
157 shared_gate_scratch,
158 &sh.up_proj,
159 shared_up_scratch,
160 inter,
161 h,
162 top_k,
163 stream,
164 )?;
165 ops::moe_expert_silu_down_shared_fp8_batch3(
166 ctx.gpu,
167 self.moe_expert_silu_down_shared_fp8_batch3,
168 expert_gate_out,
169 expert_up_out,
170 dp.weight_ptrs,
171 dp.scale_ptrs,
172 expert_down_out,
173 indices_dev,
174 shared_gate_scratch,
175 shared_up_scratch,
176 &sh.down_proj,
177 shared_down_out,
178 h,
179 inter,
180 top_k,
181 stream,
182 )?;
183 let shared_for_blend = if is_ep && !shared_down_out.is_null() {
187 ctx.gpu
188 .memset_async(expert_gate_out, 0, 3 * h as usize * 2, stream)?;
189 expert_gate_out
190 } else {
191 shared_down_out
192 };
193 ops::moe_weighted_sum_blend_batch3(
194 ctx.gpu,
195 self.moe_weighted_sum_blend_fp8_batch3,
196 output,
197 expert_down_out,
198 weights_dev,
199 shared_for_blend,
200 input,
201 self.weights.shared_expert_gate.weight,
202 h,
203 top_k,
204 h,
205 stream,
206 )?;
207 } else if self.use_t_layout_for_decode() {
208 let gate_t = self
211 .gate_ptrs_t
212 .as_ref()
213 .expect("gate_ptrs_t under unified_t");
214 let up_t = self.up_ptrs_t.as_ref().expect("up_ptrs_t under unified_t");
215 let down_t = self
216 .down_ptrs_t
217 .as_ref()
218 .expect("down_ptrs_t under unified_t");
219 let null_qw = QuantizedWeight::null();
220 let (sh_gate_t, sh_up_t, sh_down_t) = if mixed_bf16_shared {
224 (&null_qw, &null_qw, &null_qw)
225 } else {
226 (
227 self.shared_gate_t.as_ref().unwrap_or(&null_qw),
228 self.shared_up_t.as_ref().unwrap_or(&null_qw),
229 self.shared_down_t.as_ref().unwrap_or(&null_qw),
230 )
231 };
232 ops::moe_expert_gate_up_shared_batch3_t(
233 ctx.gpu,
234 self.moe_expert_gate_up_shared_batch3_t_k,
235 input,
236 gate_t.packed_ptrs,
237 gate_t.scale_ptrs,
238 gate_t.scale2_vals,
239 expert_gate_out,
240 up_t.packed_ptrs,
241 up_t.scale_ptrs,
242 up_t.scale2_vals,
243 expert_up_out,
244 indices_dev,
245 sh_gate_t,
246 shared_gate_scratch,
247 sh_up_t,
248 shared_up_scratch,
249 inter,
250 h,
251 top_k,
252 stream,
253 )?;
254 ops::moe_expert_silu_down_shared_batch3_t(
255 ctx.gpu,
256 self.moe_expert_silu_down_shared_batch3_t_k,
257 expert_gate_out,
258 expert_up_out,
259 down_t.packed_ptrs,
260 down_t.scale_ptrs,
261 down_t.scale2_vals,
262 expert_down_out,
263 indices_dev,
264 shared_gate_scratch,
265 shared_up_scratch,
266 sh_down_t,
267 shared_down_out,
268 h,
269 inter,
270 top_k,
271 stream,
272 )?;
273 if mixed_bf16_shared {
274 let shared_inter = ctx.config.shared_expert_intermediate_size as u32;
275 self.run_bf16_shared_expert(
276 input,
277 3,
278 h,
279 shared_inter,
280 shared_gate_scratch,
281 shared_up_scratch,
282 shared_down_out,
283 ctx,
284 stream,
285 )?;
286 }
287 let shared_for_blend = if is_ep && !shared_down_out.is_null() {
290 ctx.gpu
291 .memset_async(expert_gate_out, 0, 3 * h as usize * 2, stream)?;
292 expert_gate_out
293 } else {
294 shared_down_out
295 };
296 ops::moe_weighted_sum_blend_batch3(
297 ctx.gpu,
298 self.moe_weighted_sum_blend_batch3,
299 output,
300 expert_down_out,
301 weights_dev,
302 shared_for_blend,
303 input,
304 self.weights.shared_expert_gate.weight,
305 h,
306 top_k,
307 h,
308 stream,
309 )?;
310 } else {
311 ops::moe_expert_gate_up_shared_batch3(
312 ctx.gpu,
313 self.moe_expert_gate_up_shared_batch3,
314 input,
315 self.gate_ptrs.packed_ptrs,
316 self.gate_ptrs.scale_ptrs,
317 self.gate_ptrs.scale2_vals,
318 expert_gate_out,
319 self.up_ptrs.packed_ptrs,
320 self.up_ptrs.scale_ptrs,
321 self.up_ptrs.scale2_vals,
322 expert_up_out,
323 indices_dev,
324 &self.weights.shared_expert.gate_proj,
325 shared_gate_scratch,
326 &self.weights.shared_expert.up_proj,
327 shared_up_scratch,
328 inter,
329 h,
330 top_k,
331 stream,
332 )?;
333 ops::moe_expert_silu_down_shared_batch3(
334 ctx.gpu,
335 self.moe_expert_silu_down_shared_batch3,
336 expert_gate_out,
337 expert_up_out,
338 self.down_ptrs.packed_ptrs,
339 self.down_ptrs.scale_ptrs,
340 self.down_ptrs.scale2_vals,
341 expert_down_out,
342 indices_dev,
343 shared_gate_scratch,
344 shared_up_scratch,
345 &self.weights.shared_expert.down_proj,
346 shared_down_out,
347 h,
348 inter,
349 top_k,
350 stream,
351 )?;
352 let shared_for_blend = if is_ep && !shared_down_out.is_null() {
355 ctx.gpu
356 .memset_async(expert_gate_out, 0, 3 * h as usize * 2, stream)?;
357 expert_gate_out
358 } else {
359 shared_down_out
360 };
361 ops::moe_weighted_sum_blend_batch3(
362 ctx.gpu,
363 self.moe_weighted_sum_blend_batch3,
364 output,
365 expert_down_out,
366 weights_dev,
367 shared_for_blend,
368 input,
369 self.weights.shared_expert_gate.weight,
370 h,
371 top_k,
372 h,
373 stream,
374 )?;
375 }
376
377 if let Some(comm) = ctx.comm
378 && ctx.config.ep_world_size > 1
379 {
380 if ctx.graph_capture {
381 comm.all_reduce(output.0, 3 * h as usize * 2)?;
382 } else {
383 comm.all_reduce_async(output.0, 3 * h as usize * 2, stream)?;
384 }
385 if !shared_down_out.is_null() {
388 if self.weights.shared_expert_gate.weight.0 == 0 {
389 ops::residual_add(
390 ctx.gpu,
391 self.residual_add,
392 output,
393 shared_down_out,
394 3 * h,
395 stream,
396 )?;
397 } else {
398 ops::moe_batched_blend(
399 ctx.gpu,
400 self.moe_batched_blend,
401 output,
402 shared_down_out,
403 input,
404 self.weights.shared_expert_gate.weight,
405 h,
406 3,
407 stream,
408 )?;
409 }
410 }
411 }
412
413 Ok(())
414 }
415}
416
417pub(crate) fn k3_e8m0_needs_per_token(scale_kind: crate::weight_map::WeightQuantFormat) -> bool {
420 matches!(scale_kind, crate::weight_map::WeightQuantFormat::Mxfp4E8m0)
421}
422
423#[cfg(test)]
424#[path = "forward_k3_dispatch_tests.rs"]
425mod k3_dispatch_tests;