metrale_model_layers/layers/moe/
forward_token_major.rs1use super::*;
9
10impl MoeLayer {
11 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!(
29 self.router_logits_n as usize == ctx.config.num_experts,
30 "zero-expert MoE routing is not wired on this dispatch variant yet (forward_token_major)"
31 );
32 self.refuse_lean_layout("MoeLayer::forward_token_major_decode")?;
33
34 if self.lora.is_some() {
39 return self.forward_batched(input, num_tokens, ctx, stream);
40 }
41 let has_shared = self.weights.shared_expert.gate_proj.weight.0 != 0
42 && self.weights.shared_expert.up_proj.weight.0 != 0
43 && self.weights.shared_expert.down_proj.weight.0 != 0;
44 let nvfp4_supported = self.bf16_gate_weight_ptrs.is_none()
45 && !self.has_mixed_bf16_shared_expert()
46 && self.fp8_gate_weight_ptrs.is_none()
47 && !self.use_t_layout_for_decode()
48 && has_shared;
49 if !nvfp4_supported {
50 return self.forward_batched(input, num_tokens, ctx, stream);
51 }
52
53 let h = ctx.config.hidden_size as u32;
54 let inter = ctx.config.moe_intermediate_size as u32;
55 let num_experts = ctx.config.num_experts as u32;
56 let top_k = ctx.config.num_experts_per_tok as u32;
57 let n = num_tokens as u32;
58
59 let router_in = self.router_input(input, n, h, ctx, stream)?;
60 let gate_logits = ctx.buffers.gate_logits();
61 if let Some(ref nvfp4) = self.gate_nvfp4 {
62 ops::w4a16_gemm(
63 ctx.gpu,
64 self.w4a16_gemm,
65 router_in,
66 nvfp4,
67 gate_logits,
68 n,
69 num_experts,
70 h,
71 stream,
72 )?;
73 } else {
74 ops::dense_gemm(
75 ctx.gpu,
76 self.dense_gemm,
77 router_in,
78 &self.weights.gate,
79 gate_logits,
80 n,
81 num_experts,
82 h,
83 stream,
84 )?;
85 }
86
87 let scratch = ctx.buffers.scratch();
88 let indices_dev = scratch;
89 let weights_dev = scratch.offset(num_tokens * top_k as usize * 4);
90 if let Some(bias) = self.correction_bias_dev {
91 ops::moe_topk_sigmoid_batched(
92 ctx.gpu,
93 self.moe_topk_sigmoid_batched_k,
94 gate_logits,
95 bias,
96 indices_dev,
97 weights_dev,
98 num_experts,
99 top_k,
100 ctx.config.norm_topk_prob,
101 ctx.config.routed_scaling_factor as f32,
102 n,
103 stream,
104 )?;
105 } else {
106 ops::moe_topk_softmax_batched(
107 ctx.gpu,
108 self.moe_topk_batched,
109 gate_logits,
110 indices_dev,
111 weights_dev,
112 num_experts,
113 top_k,
114 ctx.config.norm_topk_prob,
115 n,
116 stream,
117 )?;
118 }
119
120 let expert_gate_out = ctx.buffers.expert_gate_out();
121 let expert_up_out = ctx.buffers.expert_up_out();
122 let expert_down_out = ctx.buffers.expert_down_out();
123 let shared_gate_scratch = ctx.buffers.logits();
124 let shared_up_scratch = ctx.buffers.ssm_qkvz();
125 let shared_down_out = ctx.buffers.attn_output();
126 let output = ctx.buffers.moe_output();
127
128 ops::moe_expert_gate_up_shared_prefill(
129 ctx.gpu,
130 self.moe_expert_gate_up_shared_token_major,
131 input,
132 self.gate_ptrs.packed_ptrs,
133 self.gate_ptrs.scale_ptrs,
134 self.gate_ptrs.scale2_vals,
135 expert_gate_out,
136 self.up_ptrs.packed_ptrs,
137 self.up_ptrs.scale_ptrs,
138 self.up_ptrs.scale2_vals,
139 expert_up_out,
140 indices_dev,
141 &self.weights.shared_expert.gate_proj,
142 shared_gate_scratch,
143 &self.weights.shared_expert.up_proj,
144 shared_up_scratch,
145 inter,
146 h,
147 top_k,
148 n,
149 stream,
150 )?;
151 ops::moe_expert_silu_down_shared_prefill(
152 ctx.gpu,
153 self.moe_expert_silu_down_shared_token_major,
154 expert_gate_out,
155 expert_up_out,
156 self.down_ptrs.packed_ptrs,
157 self.down_ptrs.scale_ptrs,
158 self.down_ptrs.scale2_vals,
159 expert_down_out,
160 indices_dev,
161 shared_gate_scratch,
162 shared_up_scratch,
163 &self.weights.shared_expert.down_proj,
164 shared_down_out,
165 h,
166 inter,
167 top_k,
168 n,
169 stream,
170 )?;
171
172 let is_ep = ctx.comm.is_some() && ctx.config.ep_world_size > 1;
173 let shared_for_blend = if is_ep {
174 ctx.gpu
175 .memset_async(expert_gate_out, 0, num_tokens * h as usize * 2, stream)?;
176 expert_gate_out
177 } else {
178 shared_down_out
179 };
180 ops::moe_weighted_sum_blend_prefill(
181 ctx.gpu,
182 self.moe_weighted_sum_blend_token_major,
183 output,
184 expert_down_out,
185 weights_dev,
186 shared_for_blend,
187 input,
188 self.weights.shared_expert_gate.weight,
189 h,
190 top_k,
191 h,
192 n,
193 stream,
194 )?;
195
196 if let Some(comm) = ctx.comm
197 && ctx.config.ep_world_size > 1
198 {
199 if ctx.graph_capture {
200 comm.all_reduce(output.0, num_tokens * h as usize * 2)?;
201 } else {
202 comm.all_reduce_async(output.0, num_tokens * h as usize * 2, stream)?;
203 }
204 if self.weights.shared_expert_gate.weight.0 == 0 {
205 ops::residual_add(
206 ctx.gpu,
207 self.residual_add,
208 output,
209 shared_down_out,
210 n * h,
211 stream,
212 )?;
213 } else {
214 ops::moe_batched_blend(
215 ctx.gpu,
216 self.moe_batched_blend,
217 output,
218 shared_down_out,
219 input,
220 self.weights.shared_expert_gate.weight,
221 h,
222 n,
223 stream,
224 )?;
225 }
226 }
227
228 Ok(())
229 }
230}