use std::time::Instant;
use half::f16;
use model_io::ResidentIndex;
use crate::families::qwen4::layer_tensor;
use crate::families::qwen4::state::RealQwen4State;
pub(crate) use crate::moe_prefill_pipeline::RoutedSlot;
use crate::real_forward_dispatch::{
encode_gemv_any, encode_moe_phase1_any, encode_moe_phase2_any, router_topk_gemma4,
};
use crate::real_forward_init::MappedResidency;
use crate::real_forward_layout::RoutedLayerLayout;
use crate::real_forward_types::{DecodeScratch, PhaseCounters, RealForwardError};
use crate::real_forward_utils::f16_slice_to_le_bytes;
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_moe_router(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
index: &ResidentIndex,
qwen4: &RealQwen4State,
mixed: (&gpu::MetalBuffer, u64),
layer: usize,
hidden: usize,
num_experts: usize,
logits_row_offset: u64,
) -> Result<(), RealForwardError> {
let gpu_err = RealForwardError::Gpu;
let router_name = layer_tensor(layer, "mlp.gate.weight");
let base = index.header.index_size;
let router = index
.entries
.get(&router_name)
.ok_or_else(|| RealForwardError::MissingTensor(router_name.clone()))?;
if router.dtype != 5 || router.size_bytes as usize != num_experts * hidden {
return Err(RealForwardError::Unsupported(format!(
"{router_name}: expected INT8 (dtype 5) {num_experts}x{hidden}, got dtype {} with \
{} packed bytes",
router.dtype, router.size_bytes
)));
}
gpu::encode_router_gemv_gemma4(
context,
pass,
(
weights.buffer(),
weights.gpu_offset(router.file_offset - base),
),
(
weights.buffer(),
weights.gpu_offset(router.scale_offset - base),
),
(
weights.buffer(),
weights.gpu_offset(router.bias_offset - base),
),
mixed,
(&qwen4.router_ones, 0),
(&qwen4.router_logits_f32, logits_row_offset),
num_experts as u32,
hidden as u32,
)
.map_err(gpu_err)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn encode_moe_layer(
context: &mut gpu::MetalContext,
pass: &gpu::PassEncoder,
weights: &gpu::ResidentGpuWeights,
index: &ResidentIndex,
scratch: &DecodeScratch,
qwen4: &RealQwen4State,
mixed: (&gpu::MetalBuffer, u64),
streamers: &mut [Option<streaming::PreadExpertStreamer>],
slot_buffers: &[Vec<gpu::MetalBuffer>],
mapped: &MappedResidency,
routed_blobs: Option<&gpu::RoutedBlobsBuffer>,
routed_blobs_banks: &[gpu::RoutedBlobsBuffer],
moe_offsets: &[gpu::MoeExpertOffsets],
routed_layouts: &[RoutedLayerLayout],
router_hist: &mut Option<crate::router_hist::RouterHistogram>,
phases: &mut PhaseCounters,
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 rw_off = slot.bank * gpu::MAX_STREAMED_EXPERTS * 2;
let t_router = Instant::now();
let router_logits = gpu::read_f32_buffer_at(
&qwen4.router_logits_f32,
slot.token * num_experts,
num_experts,
);
let (selected, route_weights) =
router_topk_gemma4(&router_logits, top_k, &qwen4.per_expert_ones);
if let Some(hist) = router_hist.as_mut() {
hist.record_router_logits(layer, &router_logits);
hist.record(layer, &selected);
}
let mapped_active = mapped.buffers.get(layer).is_some_and(Option::is_some);
let slots: Vec<usize> = if mapped_active {
phases.expert_requests += selected.len() as u64;
phases.expert_hits += selected.len() as u64;
phases.router_nanos += t_router.elapsed().as_nanos() as u64;
selected.clone()
} else {
let streamer = streamers[layer].as_mut().ok_or_else(|| {
RealForwardError::Unsupported(format!(
"real qwen4_exp layer {layer} has no packed-expert streamer"
))
})?;
let plan = streamer.plan_experts_cached(&selected, &slot.protect);
let (requests, hits) = (plan.experts.len() as u64, plan.hits as u64);
phases.expert_requests += requests;
phases.expert_hits += hits;
phases.router_nanos += t_router.elapsed().as_nanos() as u64;
let streamer = 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}")))?;
phases.expert_io_nanos += t_io.elapsed().as_nanos() as u64;
slots
};
let t_bind = Instant::now();
let ordered: Vec<(usize, f32)> = (0..selected.len())
.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(
&scratch.routing_w,
rw_off,
&f16_slice_to_le_bytes(&routing16),
);
let blob_refs: Vec<(&gpu::MetalBuffer, u64)> = if mapped_active {
let buffer = mapped.buffers[layer]
.as_ref()
.expect("mapped residency checked above");
let mapping = 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 = &slot_buffers[layer];
ordered
.iter()
.map(|&(slot, _)| (&layer_slots[slot], 0u64))
.collect()
};
let routed = match slot.bank {
0 => routed_blobs,
n => routed_blobs_banks.get(n - 1),
}
.ok_or_else(|| {
RealForwardError::Unsupported("install has no routed-blob buffer".to_string())
})?;
let offsets = &moe_offsets[layer];
let (blob_source, blob_function) = routed_layouts[layer].phase1.source_function();
routed
.bind_for(context, blob_source, blob_function, use_silu, &blob_refs)
.map_err(gpu_err)?;
phases.bind_nanos += t_bind.elapsed().as_nanos() as u64;
for &(buffer, _) in &blob_refs {
pass.use_read_buffer(buffer);
}
encode_gemv_any(
context,
pass,
weights,
index,
&layer_tensor(layer, "mlp.shared_expert.gate_proj.weight"),
inter,
hidden,
mixed,
(&scratch.ffn_gate, 0),
)?;
encode_gemv_any(
context,
pass,
weights,
index,
&layer_tensor(layer, "mlp.shared_expert.up_proj.weight"),
inter,
hidden,
mixed,
(&scratch.ffn_up, 0),
)?;
let act = if use_silu {
gpu::encode_silu_mul
} else {
gpu::encode_gelu_mul
};
act(
context,
pass,
(&scratch.ffn_gate, 0),
(&scratch.ffn_up, 0),
(&scratch.ffn_act, 0),
inter as u32,
)
.map_err(gpu_err)?;
encode_gemv_any(
context,
pass,
weights,
index,
&layer_tensor(layer, "mlp.shared_expert.down_proj.weight"),
hidden,
inter,
(&scratch.ffn_act, 0),
(&qwen4.h1, 0),
)?;
encode_gemv_any(
context,
pass,
weights,
index,
&layer_tensor(layer, "mlp.shared_expert_gate.weight"),
1,
hidden,
mixed,
(&qwen4.shared_gate_logit, 0),
)?;
gpu::encode_sigmoid_scalar_mul(
context,
pass,
(&qwen4.h1, 0),
(&qwen4.shared_gate_logit, 0),
hidden as u32,
)
.map_err(gpu_err)?;
encode_moe_phase1_any(
routed_layouts[layer].phase1,
context,
pass,
routed,
offsets,
mixed,
(&scratch.moe_acts, 0),
hidden as u32,
moe_inter,
selected.len() as u32,
use_silu,
)
.map_err(gpu_err)?;
encode_moe_phase2_any(
routed_layouts[layer].phase2,
context,
pass,
routed,
offsets,
(&scratch.moe_acts, 0),
(&scratch.routing_w, rw_off as u64),
(&qwen4.moe_zero, 0),
(&qwen4.h2, 0),
hidden as u32,
moe_inter,
top_k as u32,
use_silu,
)
.map_err(gpu_err)?;
gpu::encode_residual_add(context, pass, (&qwen4.h2, 0), (&qwen4.h1, 0), hidden as u32)
.map_err(gpu_err)?;
Ok(ordered.iter().map(|&(slot, _)| slot).collect())
}