1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
//! Routed MoE pass encoding for Gemma 4 decode flow.
use std::time::Instant;
use half::f16;
// Re-exported (not just imported) because `moe_batch.rs`, `mod.rs` and
// `shared_expert.rs` all reach this type as `moe::RoutedSlot`, and moving
// the definition to a shared module shouldn't force three call sites to
// change their qualified paths.
pub(crate) use crate::moe_prefill_pipeline::RoutedSlot;
use crate::real_forward::RealForwardRunner;
use crate::real_forward_dispatch::{
encode_moe_phase1_any, encode_moe_phase2_any, router_topk_gemma4,
};
use crate::real_forward_types::RealForwardError;
use crate::real_forward_utils::{f16_slice_to_le_bytes, layer_tensor, norm_view};
const RMS_EPS: f32 = 1e-6;
impl RealForwardRunner {
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_gemma4_layer_routed_moe(
&mut self,
pass: &gpu::PassEncoder,
layer: usize,
hidden: usize,
inter: usize,
moe_inter: u32,
num_experts: usize,
top_k: usize,
use_silu: bool,
slot: &RoutedSlot,
) -> Result<Vec<usize>, RealForwardError> {
let gpu_err = RealForwardError::Gpu;
let x_off = (slot.token * hidden * 2) as u64;
let rw_off = slot.bank * gpu::MAX_STREAMED_EXPERTS * 2;
let t_router = Instant::now();
let real = self.real.as_ref().expect("real state present");
let router_logits = gpu::read_f32_buffer_at(
&real.router_logits_f32,
slot.token * num_experts,
num_experts,
);
let (selected, route_weights) =
router_topk_gemma4(&router_logits, top_k, &real.per_expert_scale[layer]);
// The one-layer-ahead prediction THIS layer's attention encoded, read
// at the same commit boundary as the production logits above. It is
// about layer+1, so it is recorded against layer+1 and must be read
// HERE rather than there: layer+1's own attention overwrites the
// single probe buffer with its prediction about layer+2 before
// layer+1's MoE ever runs. Recording it against `layer` instead
// reads as a predictor no better than chance (7.7% against a 6.25%
// random baseline), which is what this comment exists to prevent
// anyone re-deriving.
//
// The top-k uses layer+1's `per_expert_scale`, because the logits in
// the buffer came from layer+1's router.
let next = layer
+ self
.router_hist
.as_ref()
.map_or(1, crate::router_hist::RouterHistogram::pilot_offset);
let predicted = self
.router_hist
.as_ref()
.is_some_and(crate::router_hist::RouterHistogram::pilot_enabled)
.then(|| {
let real = self.real.as_ref().expect("real state present");
real.per_expert_scale.get(next).map(|scale| {
let logits = gpu::read_f32_buffer_at(&real.pilot_logits_f32, 0, num_experts);
router_topk_gemma4(&logits, top_k, scale).0
})
})
.flatten();
if let Some(hist) = self.router_hist.as_mut() {
if let Some(predicted) = predicted {
hist.record_prediction(next, &predicted);
}
hist.record(layer, &selected);
}
let layer_scalar = real.layer_scalar[layer];
// MAPPED RESIDENCY SKIPS BOTH THE PLAN AND THE `pread`. Every expert
// is already addressable in its layer's mapping, so there is no cache
// to consult and nothing to copy -- which is the whole of what this
// mode buys (the `expert io` bucket was 3.86 ms/token at 32 slots on
// this install, and the slot cache it replaces was 1.5-3.0 GiB).
//
// `slots` carries a different KIND of handle in each arm (a cache
// slot index against an expert id), which is why the two blob-ref
// constructions below are separate rather than sharing a lookup: the
// two index different things and a shared one would read plausibly
// wrong bytes rather than failing.
let mapped_active = self.mapped.buffers.get(layer).is_some_and(Option::is_some);
let order: Vec<usize> = (0..selected.len()).collect();
let slots: Vec<usize> = if mapped_active {
// Counted as requests that all hit, because on this path every
// expert IS resident by construction. Reporting zero requests
// would make `TURBOSPARK_PHASES`'s hit rate undefined rather than
// perfect, and the two modes have to stay comparable.
self.phases.expert_requests += selected.len() as u64;
self.phases.expert_hits += selected.len() as u64;
self.phases.router_nanos += t_router.elapsed().as_nanos() as u64;
selected.clone()
} else {
let streamer = self.streamers[layer].as_mut().ok_or_else(|| {
RealForwardError::Unsupported(format!(
"real Gemma 4 layer {layer} has no packed-expert streamer"
))
})?;
// `protect` is empty on the decode path, so this is the same call it
// has always made. Inside a chunk it names the slots the previous
// token's in-flight command buffer is reading; the cache ASSERTS
// rather than degrades when it cannot honour that plus the misses
// (`ExpertCache::plan_if_possible`), so the caller has already
// ensured the arithmetic works or retired the buffer first.
let plan = streamer.plan_experts_cached(&selected, &slot.protect);
let (requests, hits) = (plan.experts.len() as u64, plan.hits as u64);
self.phases.expert_requests += requests;
self.phases.expert_hits += hits;
self.phases.router_nanos += t_router.elapsed().as_nanos() as u64;
let streamer = self.streamers[layer]
.as_mut()
.expect("streamer presence checked above");
let t_io = Instant::now();
let slots = streamer
.execute_expert_cache_plan(&plan)
.map_err(|e| RealForwardError::Unsupported(format!("expert stream: {e}")))?;
self.phases.expert_io_nanos += t_io.elapsed().as_nanos() as u64;
slots
};
if !self.shared_cb_overlap {
let h1 = self.real.as_ref().expect("real state present").h1.clone();
self.encode_shared_expert_branch(layer, hidden, inter, use_silu, slot, (&h1, 0))?;
}
let real = self.real.as_ref().expect("real state present");
let t_bind = Instant::now();
let ordered: Vec<(usize, f32)> = order
.iter()
.map(|&i| (slots[i], route_weights[i]))
.collect();
let mut routing16 = vec![f16::from_f32(0.0); gpu::MAX_STREAMED_EXPERTS];
for (dispatch_slot, &(_, weight)) in ordered.iter().enumerate() {
routing16[dispatch_slot] = f16::from_f32(weight);
}
gpu::write_buffer_bytes(
&self.scratch.routing_w,
rw_off,
&f16_slice_to_le_bytes(&routing16),
);
// The two arms differ only in WHERE a blob lives, never in the order
// the slots are dispatched: that stays the router's own ranking in
// both, which is what keeps output byte-identical across residency
// modes for the same reason it is byte-identical across slot counts
// (AGENTS.md Gotcha 27).
let blob_refs: Vec<(&gpu::MetalBuffer, u64)> = if mapped_active {
let buffer = self.mapped.buffers[layer]
.as_ref()
.expect("mapped residency checked above");
let mapping = self.mapped.layers[layer]
.as_ref()
.expect("mapped residency checked above");
ordered
.iter()
.map(|&(expert, _)| {
mapping
.expert_offset(expert)
.map(|offset| (buffer, offset))
.map_err(|e| {
RealForwardError::Unsupported(format!(
"mapped expert layer {layer}, expert {expert}: {e}"
))
})
})
.collect::<Result<Vec<_>, _>>()?
} else {
let layer_slots = &self.slot_buffers[layer];
ordered
.iter()
.map(|&(cache_slot, _)| (&layer_slots[cache_slot], 0u64))
.collect()
};
// Selected by FIELD rather than through a `&self` accessor: this
// binding is live across `routed.bind(&mut self.context, ..)` and
// the two phase encodes, and a method call would borrow all of
// `self` for that whole span (crate Gotcha 5's E0502 shape).
let routed = match slot.bank {
0 => self.routed_blobs.as_ref(),
n => self.routed_blobs_banks.get(n - 1),
}
.ok_or_else(|| {
RealForwardError::Unsupported("install has no routed-blob buffer".to_string())
})?;
let offsets = &self.moe_offsets[layer];
let layer_layout = self.routed_layouts[layer];
let (blob_source, blob_function) = layer_layout.phase1.source_function();
routed
.bind_for(
&mut self.context,
blob_source,
blob_function,
use_silu,
&blob_refs,
)
.map_err(gpu_err)?;
self.phases.bind_nanos += t_bind.elapsed().as_nanos() as u64;
for &(buffer, _) in &blob_refs {
pass.use_read_buffer(buffer);
}
let main_phase1_k = order.len();
if main_phase1_k > 0 {
encode_moe_phase1_any(
layer_layout.phase1,
&mut self.context,
pass,
routed,
offsets,
(&real.routed_x, x_off),
(&self.scratch.moe_acts, 0),
hidden as u32,
moe_inter,
main_phase1_k as u32,
use_silu,
)
.map_err(gpu_err)?;
}
encode_moe_phase2_any(
layer_layout.phase2,
&mut self.context,
pass,
routed,
offsets,
(&self.scratch.moe_acts, 0),
(&self.scratch.routing_w, rw_off as u64),
(&self.scratch.zero_hidden, 0),
(&real.h2, 0),
hidden as u32,
moe_inter,
top_k as u32,
use_silu,
)
.map_err(gpu_err)?;
let post_ffn2 = norm_view(
&self.weights,
&self.index,
&layer_tensor(layer, "post_feedforward_layernorm_2.weight"),
hidden,
)?;
gpu::encode_rms_norm_bf16w(
&mut self.context,
pass,
(&real.h2, 0),
post_ffn2,
(&real.h2, 0),
hidden as u32,
RMS_EPS,
)
.map_err(gpu_err)?;
gpu::encode_residual_add(
&mut self.context,
pass,
(&real.h1, 0),
(&real.h2, 0),
hidden as u32,
)
.map_err(gpu_err)?;
let post_ffn = norm_view(
&self.weights,
&self.index,
&layer_tensor(layer, "post_feedforward_layernorm.weight"),
hidden,
)?;
gpu::encode_rms_norm_bf16w(
&mut self.context,
pass,
(&real.h1, 0),
post_ffn,
(&self.scratch.ffn_normed, 0),
hidden as u32,
RMS_EPS,
)
.map_err(gpu_err)?;
gpu::encode_residual_add(
&mut self.context,
pass,
(&self.scratch.x, x_off),
(&self.scratch.ffn_normed, 0),
hidden as u32,
)
.map_err(gpu_err)?;
gpu::encode_scalar_mul(
&mut self.context,
pass,
(&self.scratch.x, x_off),
layer_scalar,
hidden as u32,
)
.map_err(gpu_err)?;
// The cache slots this pass BOUND, so the caller can hand them to
// the next token as `RoutedSlot::protect` while this command buffer
// is in flight. Returned rather than recomputed because the plan's
// assignment is the only thing that knows them.
Ok(ordered.iter().map(|&(cache_slot, _)| cache_slot).collect())
}
}