1use super::*;
6
7impl MoeLayer {
8 pub fn forward_token_major_decode(
20 &self,
21 input: DevicePtr,
22 num_tokens: usize,
23 ctx: &ForwardContext,
24 stream: u64,
25 ) -> Result<()> {
26 anyhow::ensure!(
30 self.router_logits_n as usize == ctx.config.num_experts,
31 "zero-expert MoE routing is not wired on this dispatch variant yet (forward_token_major)"
32 );
33
34 if self.lora.is_some() {
46 return self.forward_batched(input, num_tokens, ctx, stream);
47 }
48 let has_shared = self.weights.shared_expert.gate_proj.weight.0 != 0
49 && self.weights.shared_expert.up_proj.weight.0 != 0
50 && self.weights.shared_expert.down_proj.weight.0 != 0;
51 let nvfp4_supported = self.bf16_gate_weight_ptrs.is_none()
52 && !self.has_mixed_bf16_shared_expert()
53 && self.fp8_gate_weight_ptrs.is_none()
54 && !self.use_t_layout_for_decode()
55 && has_shared;
56 if !nvfp4_supported {
57 return self.forward_batched(input, num_tokens, ctx, stream);
58 }
59
60 let h = ctx.config.hidden_size as u32;
61 let inter = ctx.config.moe_intermediate_size as u32;
62 let num_experts = ctx.config.num_experts as u32;
63 let top_k = ctx.config.num_experts_per_tok as u32;
64 let n = num_tokens as u32;
65
66 let router_in = self.router_input(input, n, h, ctx, stream)?;
67 let gate_logits = ctx.buffers.gate_logits();
68 if let Some(ref nvfp4) = self.gate_nvfp4 {
69 ops::w4a16_gemm(
70 ctx.gpu,
71 self.w4a16_gemm,
72 router_in,
73 nvfp4,
74 gate_logits,
75 n,
76 num_experts,
77 h,
78 stream,
79 )?;
80 } else {
81 ops::dense_gemm(
82 ctx.gpu,
83 self.dense_gemm,
84 router_in,
85 &self.weights.gate,
86 gate_logits,
87 n,
88 num_experts,
89 h,
90 stream,
91 )?;
92 }
93
94 let scratch = ctx.buffers.scratch();
95 let indices_dev = scratch;
96 let weights_dev = scratch.offset(num_tokens * 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 n,
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 n,
123 stream,
124 )?;
125 }
126
127 let expert_gate_out = ctx.buffers.expert_gate_out();
128 let expert_up_out = ctx.buffers.expert_up_out();
129 let expert_down_out = ctx.buffers.expert_down_out();
130 let shared_gate_scratch = ctx.buffers.logits();
131 let shared_up_scratch = ctx.buffers.ssm_qkvz();
132 let shared_down_out = ctx.buffers.attn_output();
133 let output = ctx.buffers.moe_output();
134
135 ops::moe_expert_gate_up_shared_prefill(
136 ctx.gpu,
137 self.moe_expert_gate_up_shared_token_major,
138 input,
139 self.gate_ptrs.packed_ptrs,
140 self.gate_ptrs.scale_ptrs,
141 self.gate_ptrs.scale2_vals,
142 expert_gate_out,
143 self.up_ptrs.packed_ptrs,
144 self.up_ptrs.scale_ptrs,
145 self.up_ptrs.scale2_vals,
146 expert_up_out,
147 indices_dev,
148 &self.weights.shared_expert.gate_proj,
149 shared_gate_scratch,
150 &self.weights.shared_expert.up_proj,
151 shared_up_scratch,
152 inter,
153 h,
154 top_k,
155 n,
156 stream,
157 )?;
158 ops::moe_expert_silu_down_shared_prefill(
159 ctx.gpu,
160 self.moe_expert_silu_down_shared_token_major,
161 expert_gate_out,
162 expert_up_out,
163 self.down_ptrs.packed_ptrs,
164 self.down_ptrs.scale_ptrs,
165 self.down_ptrs.scale2_vals,
166 expert_down_out,
167 indices_dev,
168 shared_gate_scratch,
169 shared_up_scratch,
170 &self.weights.shared_expert.down_proj,
171 shared_down_out,
172 h,
173 inter,
174 top_k,
175 n,
176 stream,
177 )?;
178
179 let is_ep = ctx.comm.is_some() && ctx.config.ep_world_size > 1;
180 let shared_for_blend = if is_ep {
181 ctx.gpu
182 .memset_async(expert_gate_out, 0, num_tokens * h as usize * 2, stream)?;
183 expert_gate_out
184 } else {
185 shared_down_out
186 };
187 ops::moe_weighted_sum_blend_prefill(
188 ctx.gpu,
189 self.moe_weighted_sum_blend_token_major,
190 output,
191 expert_down_out,
192 weights_dev,
193 shared_for_blend,
194 input,
195 self.weights.shared_expert_gate.weight,
196 h,
197 top_k,
198 h,
199 n,
200 stream,
201 )?;
202
203 if let Some(comm) = ctx.comm
204 && ctx.config.ep_world_size > 1
205 {
206 if ctx.graph_capture {
207 comm.all_reduce(output.0, num_tokens * h as usize * 2)?;
208 } else {
209 comm.all_reduce_async(output.0, num_tokens * h as usize * 2, stream)?;
210 }
211 if self.weights.shared_expert_gate.weight.0 == 0 {
212 ops::residual_add(
213 ctx.gpu,
214 self.residual_add,
215 output,
216 shared_down_out,
217 n * h,
218 stream,
219 )?;
220 } else {
221 ops::moe_batched_blend(
222 ctx.gpu,
223 self.moe_batched_blend,
224 output,
225 shared_down_out,
226 input,
227 self.weights.shared_expert_gate.weight,
228 h,
229 n,
230 stream,
231 )?;
232 }
233 }
234
235 Ok(())
236 }
237}