1use super::*;
10
11mod debug_dumps;
12mod ep_reduce;
13mod route;
14mod zero_expert;
15
16impl MoeLayer {
17 pub fn forward(
18 &self,
19 input: DevicePtr,
20 ctx: &ForwardContext,
21 stream: u64,
22 ) -> Result<DevicePtr> {
23 let single_seq_decode = ctx.attn_metadata.as_ref().map_or(1, |m| m.num_seqs) <= 1;
28 if !single_seq_decode {
29 self.reject_decode_lora(ctx, "forward")?;
30 }
31 if self.nvfp4_grouped_decode_ok(1, ctx) {
34 return self
35 .forward_nvfp4_grouped_decode(input, 1, ctx, stream)
36 .map(|()| ctx.buffers.moe_output());
37 }
38 if let Some(out) = self.forward_fp8_grouped_tc_one_row(input, ctx, stream) {
40 return out;
41 }
42 if self.is_dflash_capture_layer && ctx.levers.frankenstein_decode_via_prefill {
52 if ctx.stats.once("log:moe_route") {
53 tracing::info!(
54 "FRANKENSTEIN: routing DFlash capture-layer MoE decode through forward_prefill(M=1) (one-time log)"
55 );
56 }
57 self.forward_prefill(input, 1, ctx, stream)?;
58 return Ok(ctx.buffers.moe_output());
59 }
60 self.refuse_lean_layout("MoeLayer::forward (legacy decode kernels)")?;
61
62 let h = ctx.config.hidden_size as u32;
63 let inter = ctx.config.moe_intermediate_size as u32;
64 let shared_inter = ctx.config.shared_expert_intermediate_size as u32;
65 let num_experts = ctx.config.num_experts as u32;
66 let top_k = ctx.config.num_experts_per_tok as u32;
67 let profile = ctx.profile;
68
69 macro_rules! prof {
70 ($label:expr, $body:expr) => {{
71 if profile {
72 let t = std::time::Instant::now();
73 let r = $body;
74 ctx.gpu.synchronize(stream)?;
75 tracing::info!(" MoE {}: {:.0}μs", $label, t.elapsed().as_micros());
76 r
77 } else {
78 $body
79 }
80 }};
81 }
82
83 let scratch = ctx.buffers.scratch();
84 let indices_dev = scratch;
85 let weights_dev = scratch.offset(top_k as usize * 4);
86
87 {
88 let router_in = self.router_input(input, 1, h, ctx, stream)?;
89 let gate_logits = ctx.buffers.gate_logits();
90 prof!("gate", {
91 self.decode_gate_gemv(router_in, gate_logits, h, ctx, stream)
92 })?;
93
94 if single_seq_decode {
98 self.apply_router_lora_prefill(router_in, gate_logits, 1, ctx, stream)?;
99 }
100
101 prof!("topk", {
102 if let Some(tid2eid) = self.tid2eid_dev {
103 let token_ids = ctx.token_ids.ok_or_else(|| {
107 anyhow::anyhow!(
108 "DeepSeek-V4 hash-MoE layer requires ForwardContext.token_ids (decode)"
109 )
110 })?;
111 ops::moe_hash_route(
112 ctx.gpu,
113 self.moe_hash_route_k,
114 gate_logits,
115 tid2eid,
116 token_ids,
117 indices_dev,
118 weights_dev,
119 num_experts,
120 top_k,
121 ctx.config.norm_topk_prob,
122 ctx.config.routed_scaling_factor as f32,
123 stream,
124 )
125 } else {
126 self.decode_topk_scored(
127 gate_logits,
128 indices_dev,
129 weights_dev,
130 num_experts,
131 top_k,
132 ctx,
133 stream,
134 )
135 }
136 })?;
137 }
138
139 if tracing::enabled!(tracing::Level::DEBUG) && !ctx.graph_capture {
140 debug_dumps::debug_routing(ctx, indices_dev, weights_dev, top_k, stream)?;
141 }
142
143 let expert_input = if let Some(ref norm_w) = self.pre_expert_norm {
147 let normed = ctx.buffers.ssm_deinterleaved();
148 let eps = ctx.config.rms_norm_eps as f32;
149 prof!("pre_expert_norm", {
150 ops::rms_norm(
151 ctx.gpu,
152 self.pre_expert_norm_k,
153 input,
154 norm_w,
155 normed,
156 1,
157 h,
158 eps,
159 stream,
160 )
161 })?;
162 normed
163 } else {
164 input
165 };
166
167 let expert_gate_out = ctx.buffers.expert_gate_out();
168 let expert_up_out = ctx.buffers.expert_up_out();
169 let expert_down_out = ctx.buffers.expert_down_out();
170 let shared_gate_scratch = ctx.buffers.logits();
174 let shared_up_scratch = ctx.buffers.ssm_qkvz();
175 let shared_out = ctx.buffers.attn_output();
176
177 if let (Some(gp), Some(up), Some(dp), Some(shared)) = (
178 self.bf16_gate_weight_ptrs,
179 self.bf16_up_weight_ptrs,
180 self.bf16_down_weight_ptrs,
181 self.bf16_shared_expert,
182 ) {
183 prof!("exp_gate_up_bf16", {
185 ops::moe_expert_gate_up_shared_bf16(
186 ctx.gpu,
187 self.moe_expert_gate_up_shared_bf16_k,
188 expert_input,
189 gp,
190 expert_gate_out,
191 up,
192 expert_up_out,
193 indices_dev,
194 shared.gate_proj.weight,
195 shared_gate_scratch,
196 shared.up_proj.weight,
197 shared_up_scratch,
198 inter,
199 h,
200 top_k,
201 stream,
202 )
203 })?;
204 if single_seq_decode {
205 self.apply_expert_lora_decode_gateup(
206 expert_gate_out,
207 expert_up_out,
208 expert_input,
209 indices_dev,
210 top_k,
211 top_k,
212 DevicePtr::NULL,
213 ctx,
214 stream,
215 )?;
216 }
217 prof!("exp_silu_down_bf16", {
218 ops::moe_expert_silu_down_shared_bf16(
219 ctx.gpu,
220 self.moe_expert_silu_down_shared_bf16_k,
221 expert_gate_out,
222 expert_up_out,
223 dp,
224 expert_down_out,
225 indices_dev,
226 shared_gate_scratch,
227 shared_up_scratch,
228 shared.down_proj.weight,
229 shared_out,
230 h,
231 inter,
232 top_k,
233 stream,
234 )
235 })?;
236 } else if let (Some(gp), Some(up), Some(dp), Some(sh)) = (
237 &self.fp8_gate_weight_ptrs,
238 &self.fp8_up_weight_ptrs,
239 &self.fp8_down_weight_ptrs,
240 &self.fp8_shared_expert,
241 ) {
242 prof!("exp_gate_up_fp8", {
243 ops::moe_expert_gate_up_shared_fp8(
244 ctx.gpu,
245 self.moe_expert_gate_up_shared_fp8,
246 expert_input,
247 gp.weight_ptrs,
248 gp.scale_ptrs,
249 expert_gate_out,
250 up.weight_ptrs,
251 up.scale_ptrs,
252 expert_up_out,
253 indices_dev,
254 &sh.gate_proj,
255 shared_gate_scratch,
256 &sh.up_proj,
257 shared_up_scratch,
258 inter,
259 h,
260 top_k,
261 stream,
262 )
263 })?;
264
265 if single_seq_decode {
266 self.apply_expert_lora_decode_gateup(
267 expert_gate_out,
268 expert_up_out,
269 expert_input,
270 indices_dev,
271 top_k,
272 top_k,
273 DevicePtr::NULL,
274 ctx,
275 stream,
276 )?;
277 }
278 prof!("exp_silu_down_fp8", {
279 ops::moe_expert_silu_down_shared_fp8(
280 ctx.gpu,
281 self.moe_expert_silu_down_shared_fp8,
282 expert_gate_out,
283 expert_up_out,
284 dp.weight_ptrs,
285 dp.scale_ptrs,
286 expert_down_out,
287 indices_dev,
288 shared_gate_scratch,
289 shared_up_scratch,
290 &sh.down_proj,
291 shared_out,
292 h,
293 inter,
294 top_k,
295 stream,
296 )
297 })?;
298 } else if self.use_t_layout_for_decode() {
299 prof!("exp_unified_t", {
300 self.dispatch_unified_t_decode(
301 ctx,
302 expert_input,
303 expert_gate_out,
304 expert_up_out,
305 expert_down_out,
306 shared_gate_scratch,
307 shared_up_scratch,
308 shared_out,
309 indices_dev,
310 h,
311 inter,
312 top_k,
313 single_seq_decode,
314 stream,
315 )
316 })?;
317 } else {
318 prof!("exp_gate_up", {
319 ops::moe_expert_gate_up_shared(
320 ctx.gpu,
321 self.moe_expert_gate_up_shared,
322 expert_input,
323 self.gate_ptrs.packed_ptrs,
324 self.gate_ptrs.scale_ptrs,
325 self.gate_ptrs.scale2_vals,
326 expert_gate_out,
327 self.up_ptrs.packed_ptrs,
328 self.up_ptrs.scale_ptrs,
329 self.up_ptrs.scale2_vals,
330 expert_up_out,
331 indices_dev,
332 &self.weights.shared_expert.gate_proj,
333 shared_gate_scratch,
334 &self.weights.shared_expert.up_proj,
335 shared_up_scratch,
336 inter,
337 h,
338 top_k,
339 stream,
340 )
341 })?;
342
343 if single_seq_decode {
344 self.apply_expert_lora_decode_gateup(
345 expert_gate_out,
346 expert_up_out,
347 expert_input,
348 indices_dev,
349 top_k,
350 top_k,
351 DevicePtr::NULL,
352 ctx,
353 stream,
354 )?;
355 }
356
357 if tracing::enabled!(tracing::Level::DEBUG) && !ctx.graph_capture {
358 debug_dumps::debug_expert_intermediates(
359 ctx,
360 expert_gate_out,
361 expert_up_out,
362 shared_gate_scratch,
363 shared_up_scratch,
364 stream,
365 )?;
366 }
367
368 prof!("exp_silu_down", {
369 ops::moe_expert_silu_down_shared(
370 ctx.gpu,
371 self.moe_expert_silu_down_shared,
372 expert_gate_out,
373 expert_up_out,
374 self.down_ptrs.packed_ptrs,
375 self.down_ptrs.scale_ptrs,
376 self.down_ptrs.scale2_vals,
377 expert_down_out,
378 indices_dev,
379 shared_gate_scratch,
380 shared_up_scratch,
381 &self.weights.shared_expert.down_proj,
382 shared_out,
383 h,
384 inter,
385 top_k,
386 stream,
387 )
388 })?;
389 }
390
391 if single_seq_decode {
398 self.apply_expert_lora_decode_down(
399 expert_gate_out,
400 expert_up_out,
401 expert_down_out,
402 indices_dev,
403 top_k,
404 top_k,
405 DevicePtr::NULL,
406 ctx,
407 stream,
408 )?;
409 }
410
411 if self.has_mixed_bf16_shared_expert() {
412 self.run_bf16_shared_expert(
413 input,
414 1,
415 h,
416 shared_inter,
417 shared_gate_scratch,
418 shared_up_scratch,
419 shared_out,
420 ctx,
421 stream,
422 )?;
423 }
424
425 if tracing::enabled!(tracing::Level::DEBUG) && !ctx.graph_capture {
426 ctx.gpu.synchronize(stream)?;
427 let mut down_buf = vec![0u8; 16];
428 ctx.gpu.copy_d2h(expert_down_out, &mut down_buf)?;
429 let down_vals: Vec<f32> = (0..8)
430 .map(|i| {
431 let bits = u16::from_le_bytes([down_buf[i * 2], down_buf[i * 2 + 1]]);
432 f32::from_bits((bits as u32) << 16)
433 })
434 .collect();
435 tracing::info!(" MoE down_out[slot0,0..8]: {:?}", down_vals);
436 let mut sh_buf = vec![0u8; 16];
437 ctx.gpu.copy_d2h(shared_out, &mut sh_buf)?;
438 let sh_vals: Vec<f32> = (0..8)
439 .map(|i| {
440 let bits = u16::from_le_bytes([sh_buf[i * 2], sh_buf[i * 2 + 1]]);
441 f32::from_bits((bits as u32) << 16)
442 })
443 .collect();
444 tracing::info!(" MoE shared_out[0..8]: {:?}", sh_vals);
445 }
446
447 let output = ctx.buffers.moe_output();
454 let is_ep = ctx.comm.is_some() && ctx.config.ep_world_size > 1;
455 let shared_for_blend = if is_ep && !shared_out.is_null() {
456 let zero_buf = ctx.buffers.expert_gate_out();
457 ctx.gpu.memset_async(zero_buf, 0, h as usize * 2, stream)?;
458 zero_buf
459 } else {
460 shared_out
461 };
462 prof!("wsum_blend", {
463 ops::moe_weighted_sum_blend(
464 ctx.gpu,
465 self.moe_weighted_sum_blend,
466 output,
467 expert_down_out,
468 weights_dev,
469 shared_for_blend,
470 input,
471 self.weights.shared_expert_gate.weight,
472 h,
473 top_k,
474 h,
475 stream,
476 )
477 })?;
478
479 self.ep_reduce_shared(output, shared_out, input, h, ctx, stream)?;
482
483 if tracing::enabled!(tracing::Level::DEBUG) && !ctx.graph_capture {
484 ctx.gpu.synchronize(stream)?;
485 let mut buf = vec![0u8; 8];
486 ctx.gpu.copy_d2h(output, &mut buf)?;
487 let vals: Vec<f32> = (0..4)
488 .map(|i| {
489 let lo = buf[i * 2];
490 let hi = buf[i * 2 + 1];
491 f32::from_bits(((lo as u32) | ((hi as u32) << 8)) << 16)
492 })
493 .collect();
494 tracing::info!(" MoE output: {:?}", vals);
495 }
496
497 Ok(output)
498 }
499}