metrale_model_layers/layers/moe/
forward_bf16_grouped_decode.rs1use super::forward_fp8_grouped_decode::grouped_decode_buffer_need;
16use super::forward_nvfp4_grouped_decode::NVFP4_GROUPED_DECODE_TC_MAX_ROWS;
17use super::*;
18
19impl MoeLayer {
20 pub fn bf16_grouped_decode_arena_ok(
24 &self,
25 m: usize,
26 cfg: &metrale_config::ModelConfig,
27 b: &metrale_gpu_runtime::buffers::BufferArena,
28 ) -> bool {
29 let (h, inter) = (cfg.hidden_size, cfg.moe_intermediate_size);
30 let need =
31 grouped_decode_buffer_need(m, h, inter, cfg.num_experts, cfg.num_experts_per_tok);
32 let k = &self.nvfp4_grouped;
33 (1..=NVFP4_GROUPED_DECODE_TC_MAX_ROWS).contains(&m)
34 && ops::bf16_grouped_tc_shape_ok(inter as u32, h as u32, ops::BF16_GROUPED_GATE_UP_TC)
35 && ops::bf16_grouped_tc_shape_ok(h as u32, inter as u32, ops::BF16_GROUPED_DOWN_TC)
36 && k.bf16_gate_up_tc.0 != 0
37 && k.bf16_down_tc.0 != 0
38 && self.bf16_gate_weight_ptrs.is_some()
39 && self.bf16_up_weight_ptrs.is_some()
40 && self.bf16_down_weight_ptrs.is_some()
41 && self.bf16_shared_expert.is_some()
42 && self.moe_weighted_sum_blend_fp8_grouped_k.0 != 0
43 && self.moe_fp8_grouped_sort_k.0 != 0
44 && self.moe_topk_softmax_rows_k.0 != 0
45 && self.router_gemv_batchm_k.0 != 0
46 && cfg.num_experts <= ops::FP8_GROUPED_SORT_MAX_EXPERTS as usize
47 && self.router_logits_n as usize == cfg.num_experts
48 && self.gate_nvfp4.is_none()
49 && self.correction_bias_dev.is_none()
50 && self.weights.router_pre_norm.is_none()
51 && self.lora.is_none()
52 && self.pre_expert_norm.is_none()
53 && self.tid2eid_dev.is_none()
54 && h.is_multiple_of(8)
55 && arena_fits(&need, b)
56 }
57
58 pub fn bf16_grouped_decode_ok(&self, m: usize, ctx: &ForwardContext) -> bool {
61 let cfg = ctx.config;
62 let refused = ctx.levers.fp32_gate
63 || self.fp32_routing_active(ctx.levers)
64 || (self.is_dflash_capture_layer && ctx.levers.frankenstein_decode_via_prefill)
65 || (ctx.comm.is_some() && cfg.ep_world_size > 1);
66 self.bf16_grouped_decode_arena_ok(m, cfg, ctx.buffers) && !refused
67 }
68
69 pub fn forward_bf16_grouped_decode(
73 &self,
74 input: DevicePtr,
75 m: usize,
76 ctx: &ForwardContext,
77 stream: u64,
78 ) -> Result<()> {
79 anyhow::ensure!(
80 self.bf16_grouped_decode_ok(m, ctx),
81 "forward_bf16_grouped_decode: predicate false for m={m} (caller must gate on it)"
82 );
83 let (Some(gp), Some(up), Some(dp), Some(sh)) = (
84 self.bf16_gate_weight_ptrs,
85 self.bf16_up_weight_ptrs,
86 self.bf16_down_weight_ptrs,
87 self.bf16_shared_expert.as_ref(),
88 ) else {
89 anyhow::bail!("forward_bf16_grouped_decode: BF16 experts missing");
90 };
91 let h = ctx.config.hidden_size as u32;
92 let inter = ctx.config.moe_intermediate_size as u32;
93 let num_experts = ctx.config.num_experts as u32;
94 let top_k = ctx.config.num_experts_per_tok as u32;
95 let n = m as u32;
96 let te = m * top_k as usize;
97 if ctx.stats.once("log:moe_bf16_grouped_decode") {
98 tracing::info!(
99 "MoE BF16 grouped decode active (first use M={m}, top_k={top_k}, \
100 experts={num_experts}; one-time log)"
101 );
102 }
103 let scratch = ctx.buffers.scratch();
104 let indices_dev = scratch;
105 let weights_dev = scratch.offset(te * 4);
106 self.grouped_route(
107 input,
108 m,
109 GroupedRouting::PerRow,
110 indices_dev,
111 weights_dev,
112 ctx,
113 stream,
114 )?;
115 let ne = num_experts as usize;
117 let gate_logits = ctx.buffers.gate_logits();
118 let sorted_token_ids = gate_logits;
119 let sorted_expert_ids = gate_logits.offset(te * 4);
120 let expert_offsets = gate_logits.offset(te * 4 * 2);
121 let token_to_perm = gate_logits.offset(te * 4 * 2 + (ne + 1) * 4);
122 let cap = ops::fp8_grouped_active_cap(n, top_k, num_experts);
123 let active_experts = token_to_perm.offset(te * 4);
124 let active_count = active_experts.offset(cap as usize * 4);
125 ops::moe_fp8_grouped_sort(
126 ctx.gpu,
127 self.moe_fp8_grouped_sort_k,
128 ops::Fp8GroupedSortOut {
129 sorted_token_ids,
130 sorted_expert_ids,
131 expert_offsets,
132 token_to_perm,
133 active_experts,
134 active_count,
135 },
136 indices_dev,
137 te as u32,
138 num_experts,
139 top_k,
140 stream,
141 )?;
142 let act = ctx.buffers.expert_gate_out();
143 let expert_down_out = ctx.buffers.expert_down_out();
144 let shared_act = ctx.buffers.logits();
145 let shared_out = ctx.buffers.attn_output();
146 let k = &self.nvfp4_grouped;
147 ops::moe_expert_gate_up_act_bf16_grouped(
148 ctx.gpu,
149 k.bf16_gate_up_tc,
150 input,
151 gp,
152 up,
153 act,
154 expert_offsets,
155 sorted_token_ids,
156 active_experts,
157 active_count,
158 sh.gate_proj.weight,
159 sh.up_proj.weight,
160 shared_act,
161 inter,
162 h,
163 cap,
164 n,
165 stream,
166 )?;
167 ops::moe_expert_down_act_bf16_grouped(
168 ctx.gpu,
169 k.bf16_down_tc,
170 act,
171 dp,
172 expert_down_out,
173 expert_offsets,
174 active_experts,
175 active_count,
176 shared_act,
177 sh.down_proj.weight,
178 shared_out,
179 h,
180 inter,
181 cap,
182 n,
183 stream,
184 )?;
185 ops::moe_weighted_sum_blend_fp8_grouped(
186 ctx.gpu,
187 self.moe_weighted_sum_blend_fp8_grouped_k,
188 ctx.buffers.moe_output(),
189 expert_down_out,
190 weights_dev,
191 token_to_perm,
192 shared_out,
193 input,
194 self.weights.shared_expert_gate.weight,
195 h,
196 top_k,
197 h,
198 n,
199 stream,
200 )
201 }
202
203 pub fn any_grouped_decode_ok(&self, m: usize, ctx: &ForwardContext) -> bool {
206 if self.nvfp4_grouped.declared_experts {
207 self.nvfp4_grouped_decode_ok(m, ctx)
208 } else if self.fp8_gate_weight_ptrs.is_some() {
209 self.fp8_grouped_decode_ok(m, ctx)
210 } else {
211 self.bf16_grouped_decode_ok(m, ctx)
212 }
213 }
214
215 pub fn any_grouped_decode_arena_ok(
217 &self,
218 m: usize,
219 cfg: &metrale_config::ModelConfig,
220 b: &metrale_gpu_runtime::buffers::BufferArena,
221 ) -> bool {
222 if self.nvfp4_grouped.declared_experts {
223 (1..=NVFP4_GROUPED_DECODE_TC_MAX_ROWS).contains(&m)
225 && arena_fits(
226 &grouped_decode_buffer_need(
227 m,
228 cfg.hidden_size,
229 cfg.moe_intermediate_size,
230 cfg.num_experts,
231 cfg.num_experts_per_tok,
232 ),
233 b,
234 )
235 } else if self.fp8_gate_weight_ptrs.is_some() {
236 self.fp8_grouped_decode_arena_ok(m, cfg, b)
237 } else {
238 self.bf16_grouped_decode_arena_ok(m, cfg, b)
239 }
240 }
241
242 pub fn forward_any_grouped_decode(
244 &self,
245 input: DevicePtr,
246 m: usize,
247 ctx: &ForwardContext,
248 stream: u64,
249 ) -> Result<()> {
250 if self.nvfp4_grouped.declared_experts {
251 self.forward_nvfp4_grouped_decode(input, m, ctx, stream)
252 } else if self.fp8_gate_weight_ptrs.is_some() {
253 self.forward_fp8_grouped_decode(input, m, ctx, stream)
254 } else {
255 self.forward_bf16_grouped_decode(input, m, ctx, stream)
256 }
257 }
258}
259
260fn arena_fits(
262 need: &super::forward_fp8_grouped_decode::GroupedDecodeBufferNeed,
263 b: &metrale_gpu_runtime::buffers::BufferArena,
264) -> bool {
265 b.scratch_bytes() >= need.scratch
266 && b.gate_logits_bytes() >= need.gate_logits
267 && b.expert_gate_out_bytes() >= need.expert_gate_out
268 && b.expert_down_out_bytes() >= need.expert_down_out
269 && b.logits_bytes() >= need.shared_act
270 && b.attn_output_bytes() >= need.row_hidden
271 && b.moe_output_bytes() >= need.row_hidden
272}