1use anyhow::Context as _;
9
10use super::*;
11
12mod originals;
13mod unified_t;
14
15impl MoeLayer {
16 pub fn forward_k2(
24 &self,
25 input: DevicePtr, ctx: &ForwardContext,
27 stream: u64,
28 ) -> Result<()> {
29 if self.nvfp4_grouped_decode_ok(2, ctx) {
32 return self.forward_nvfp4_grouped_decode(input, 2, ctx, stream);
33 }
34 self.refuse_lean_layout("MoeLayer::forward_k2")?;
35 anyhow::ensure!(
38 self.router_logits_n as usize == ctx.config.num_experts,
39 "zero-expert MoE routing is not wired on this dispatch variant yet (forward_k2)"
40 );
41
42 if self.lora.is_some() {
46 return self.forward_batched(input, 2, ctx, stream);
47 }
48 let is_ep = ctx.comm.is_some() && ctx.config.ep_world_size > 1;
53 let use_bf16_batch2 = self.bf16_gate_weight_ptrs.is_some()
54 && self.moe_expert_gate_up_shared_bf16_batch2_k.0 != 0
55 && self.moe_expert_silu_down_shared_bf16_batch2_k.0 != 0
56 && !is_ep;
57 if self.bf16_gate_weight_ptrs.is_some() && !use_bf16_batch2 {
58 return self.forward_batched(input, 2, ctx, stream);
59 }
60 if k2_e8m0_needs_per_token(self.experts_scale_kind) {
67 return self.forward_batched(input, 2, ctx, stream);
68 }
69 let mixed_bf16_shared = self.has_mixed_bf16_shared_expert();
75 let mixed_t_ok = self.use_t_layout_for_decode()
76 && self.moe_expert_gate_up_shared_batch2_t_k.0 != 0
77 && self.moe_expert_silu_down_shared_batch2_t_k.0 != 0;
78 let mixed_orig_ok = !self.use_t_layout_for_decode()
79 && self.moe_expert_gate_up_shared_batch2.0 != 0
80 && self.moe_expert_silu_down_shared_batch2.0 != 0
81 && !self.gate_ptrs.packed_ptrs.is_null();
82 if mixed_bf16_shared && !((mixed_t_ok || mixed_orig_ok) && !is_ep) {
83 return self.forward_batched(input, 2, ctx, stream);
84 }
85
86 let h = ctx.config.hidden_size as u32;
87 let inter = ctx.config.moe_intermediate_size as u32;
88 let num_experts = ctx.config.num_experts as u32;
89 let top_k = ctx.config.num_experts_per_tok as u32;
90
91 let k2_diag = ctx.levers.k2_diag;
94 if k2_diag {
95 ctx.gpu
96 .synchronize(stream)
97 .context("K2 ENTRY: attention+norm BEFORE forward_k2")?;
98 }
99
100 let router_in = self.router_input(input, 2, h, ctx, stream)?;
101 let gate_logits = ctx.buffers.gate_logits();
102 if let Some(ref nvfp4) = self.gate_nvfp4 {
103 ops::w4a16_gemv_batch2(
104 ctx.gpu,
105 self.w4a16_gemv_batch2,
106 router_in,
107 nvfp4,
108 gate_logits,
109 num_experts,
110 h,
111 stream,
112 )?;
113 } else {
114 self.router_gemm_bf16(router_in, gate_logits, 2, num_experts, h, ctx, stream)?;
115 }
116
117 let scratch = ctx.buffers.scratch();
119 let indices_dev = scratch;
120 let weights_dev = scratch.offset(2 * top_k as usize * 4);
121 if let Some(bias) = self.correction_bias_dev {
122 if ctx.config.scoring_func == "sqrtsoftplus" {
126 for t in 0..2usize {
129 ops::moe_topk_sqrtsoftplus(
130 ctx.gpu,
131 self.moe_topk_sqrtsoftplus_k,
132 gate_logits.offset(t * num_experts as usize * 2),
133 bias,
134 indices_dev.offset(t * top_k as usize * 4),
135 weights_dev.offset(t * top_k as usize * 4),
136 num_experts,
137 top_k,
138 ctx.config.norm_topk_prob,
139 ctx.config.routed_scaling_factor as f32,
140 stream,
141 )?;
142 }
143 } else {
144 ops::moe_topk_sigmoid_batched(
145 ctx.gpu,
146 self.moe_topk_sigmoid_batched_k,
147 gate_logits,
148 bias,
149 indices_dev,
150 weights_dev,
151 num_experts,
152 top_k,
153 ctx.config.norm_topk_prob,
154 ctx.config.routed_scaling_factor as f32,
155 2,
156 stream,
157 )?;
158 }
159 } else {
160 ops::moe_topk_softmax_batched(
161 ctx.gpu,
162 self.moe_topk_batched,
163 gate_logits,
164 indices_dev,
165 weights_dev,
166 num_experts,
167 top_k,
168 ctx.config.norm_topk_prob,
169 2,
170 stream,
171 )?;
172 }
173 super::union_stats::maybe_sample_expert_union(ctx, indices_dev, 2, top_k as usize, stream);
174
175 if k2_diag {
176 ctx.gpu
177 .synchronize(stream)
178 .context("K2: gate-GEMV + topk")?;
179 }
180
181 let expert_gate_out = ctx.buffers.expert_gate_out();
182 let expert_up_out = ctx.buffers.expert_up_out();
183 let shared_gate_scratch = ctx.buffers.logits();
184 let shared_up_scratch = ctx.buffers.ssm_qkvz();
185 let expert_down_out = ctx.buffers.expert_down_out();
186 let shared_down_out = ctx.buffers.attn_output();
187 let output = ctx.buffers.moe_output();
188
189 if use_bf16_batch2
190 && let (Some(gp), Some(up), Some(dp), Some(shared)) = (
191 self.bf16_gate_weight_ptrs,
192 self.bf16_up_weight_ptrs,
193 self.bf16_down_weight_ptrs,
194 self.bf16_shared_expert,
195 )
196 {
197 ops::moe_expert_gate_up_shared_bf16_batch2(
199 ctx.gpu,
200 self.moe_expert_gate_up_shared_bf16_batch2_k,
201 input,
202 gp,
203 expert_gate_out,
204 up,
205 expert_up_out,
206 indices_dev,
207 shared.gate_proj.weight,
208 shared_gate_scratch,
209 shared.up_proj.weight,
210 shared_up_scratch,
211 inter,
212 h,
213 top_k,
214 stream,
215 )?;
216 ops::moe_expert_silu_down_shared_bf16_batch2(
217 ctx.gpu,
218 self.moe_expert_silu_down_shared_bf16_batch2_k,
219 expert_gate_out,
220 expert_up_out,
221 dp,
222 expert_down_out,
223 indices_dev,
224 shared_gate_scratch,
225 shared_up_scratch,
226 shared.down_proj.weight,
227 shared_down_out,
228 h,
229 inter,
230 top_k,
231 stream,
232 )?;
233 ops::moe_weighted_sum_blend_batch2(
234 ctx.gpu,
235 self.moe_weighted_sum_blend_batch2,
236 output,
237 expert_down_out,
238 weights_dev,
239 shared_down_out,
240 input,
241 self.weights.shared_expert_gate.weight,
242 h,
243 top_k,
244 h,
245 stream,
246 )?;
247 } else if let (Some(gp), Some(up), Some(dp), Some(sh)) = (
248 &self.fp8_gate_weight_ptrs,
249 &self.fp8_up_weight_ptrs,
250 &self.fp8_down_weight_ptrs,
251 &self.fp8_shared_expert,
252 ) {
253 ops::moe_expert_gate_up_shared_fp8_batch2(
254 ctx.gpu,
255 self.moe_expert_gate_up_shared_fp8_batch2,
256 input,
257 gp.weight_ptrs,
258 gp.scale_ptrs,
259 expert_gate_out,
260 up.weight_ptrs,
261 up.scale_ptrs,
262 expert_up_out,
263 indices_dev,
264 &sh.gate_proj,
265 shared_gate_scratch,
266 &sh.up_proj,
267 shared_up_scratch,
268 inter,
269 h,
270 top_k,
271 stream,
272 )?;
273 ops::moe_expert_silu_down_shared_fp8_batch2(
274 ctx.gpu,
275 self.moe_expert_silu_down_shared_fp8_batch2,
276 expert_gate_out,
277 expert_up_out,
278 dp.weight_ptrs,
279 dp.scale_ptrs,
280 expert_down_out,
281 indices_dev,
282 shared_gate_scratch,
283 shared_up_scratch,
284 &sh.down_proj,
285 shared_down_out,
286 h,
287 inter,
288 top_k,
289 stream,
290 )?;
291 let shared_for_blend = if is_ep && !shared_down_out.is_null() {
295 ctx.gpu
296 .memset_async(expert_gate_out, 0, 2 * h as usize * 2, stream)?;
297 expert_gate_out
298 } else {
299 shared_down_out
300 };
301 ops::moe_weighted_sum_blend_batch2(
302 ctx.gpu,
303 self.moe_weighted_sum_blend_fp8_batch2,
304 output,
305 expert_down_out,
306 weights_dev,
307 shared_for_blend,
308 input,
309 self.weights.shared_expert_gate.weight,
310 h,
311 top_k,
312 h,
313 stream,
314 )?;
315 } else if self.use_t_layout_for_decode() {
316 self.forward_k2_unified_t(
317 input,
318 indices_dev,
319 weights_dev,
320 expert_gate_out,
321 expert_up_out,
322 expert_down_out,
323 shared_gate_scratch,
324 shared_up_scratch,
325 shared_down_out,
326 output,
327 inter,
328 h,
329 top_k,
330 is_ep,
331 mixed_bf16_shared,
332 ctx,
333 stream,
334 )?;
335 } else {
336 self.forward_k2_originals(
337 input,
338 indices_dev,
339 weights_dev,
340 expert_gate_out,
341 expert_up_out,
342 expert_down_out,
343 shared_gate_scratch,
344 shared_up_scratch,
345 shared_down_out,
346 output,
347 inter,
348 h,
349 top_k,
350 is_ep,
351 mixed_bf16_shared,
352 ctx,
353 stream,
354 )?;
355 }
356
357 if k2_diag {
358 ctx.gpu
359 .synchronize(stream)
360 .context("K2: expert dispatch (gate_up/silu_down/blend)")?;
361 }
362
363 if let Some(comm) = ctx.comm
364 && ctx.config.ep_world_size > 1
365 {
366 if ctx.graph_capture {
367 comm.all_reduce(output.0, 2 * h as usize * 2)?;
368 } else {
369 comm.all_reduce_async(output.0, 2 * h as usize * 2, stream)?;
370 }
371 if !shared_down_out.is_null() {
374 if self.weights.shared_expert_gate.weight.0 == 0 {
375 ops::residual_add(
376 ctx.gpu,
377 self.residual_add,
378 output,
379 shared_down_out,
380 2 * h,
381 stream,
382 )?;
383 } else {
384 ops::moe_batched_blend(
385 ctx.gpu,
386 self.moe_batched_blend,
387 output,
388 shared_down_out,
389 input,
390 self.weights.shared_expert_gate.weight,
391 h,
392 2,
393 stream,
394 )?;
395 }
396 }
397 }
398
399 Ok(())
400 }
401}
402
403mod forward_k2_helpers;
404pub(crate) use forward_k2_helpers::{batch2_block_width, k2_e8m0_needs_per_token};
405
406#[cfg(test)]
407#[path = "forward_k2_dispatch_tests.rs"]
408mod k2_dispatch_tests;