use model_io::ArchConfig;
pub(crate) struct BatchedScratch {
pub(crate) batch: usize,
pub(crate) normed: gpu::MetalBuffer,
pub(crate) q_packed: gpu::MetalBuffer,
pub(crate) q: gpu::MetalBuffer,
pub(crate) attn_gate: gpu::MetalBuffer,
pub(crate) attn_out: gpu::MetalBuffer,
pub(crate) o: gpu::MetalBuffer,
pub(crate) gdn_qkv_raw: gpu::MetalBuffer,
pub(crate) gdn_conv_out: gpu::MetalBuffer,
pub(crate) gdn_z: gpu::MetalBuffer,
pub(crate) gdn_a: gpu::MetalBuffer,
pub(crate) gdn_b: gpu::MetalBuffer,
pub(crate) gdn_y: gpu::MetalBuffer,
pub(crate) gdn_out: gpu::MetalBuffer,
pub(crate) moe_x: gpu::MetalBuffer,
pub(crate) ffn_gate: gpu::MetalBuffer,
pub(crate) ffn_up: gpu::MetalBuffer,
pub(crate) ffn_act: gpu::MetalBuffer,
pub(crate) h2: gpu::MetalBuffer,
pub(crate) logits: gpu::MetalBuffer,
pub(crate) routed: Option<BatchedRoutedScratch>,
pub(crate) verify_tape: Option<BatchedVerifyTape>,
pub(crate) steel_gemm: bool,
pub(crate) rows_attention: Option<gpu::GqaRowsScratch>,
}
pub(crate) struct BatchedVerifyTape {
pub(crate) qkv_raw: gpu::MetalBuffer,
pub(crate) a: gpu::MetalBuffer,
pub(crate) b: gpu::MetalBuffer,
}
pub(crate) struct BatchedRoutedScratch {
pub(crate) batch_acts: gpu::MetalBuffer,
pub(crate) batch_y: gpu::MetalBuffer,
pub(crate) batch_h1: gpu::MetalBuffer,
pub(crate) batch_gate_logit: gpu::MetalBuffer,
pub(crate) batch_routing_w: gpu::MetalBuffer,
pub(crate) batch_routes: gpu::MetalBuffer,
pub(crate) batch_router_logits_f32: gpu::MetalBuffer,
pub(crate) wide_blobs: gpu::RoutedBlobsWideBuffer,
}
impl BatchedScratch {
pub(crate) fn new(
context: &mut gpu::MetalContext,
arch: &ArchConfig,
qwen_shape: gpu::GdnShape,
batch: usize,
record_tape: bool,
) -> Result<Self, gpu::GpuError> {
let hidden = arch.hidden_size as u64;
let q_dim = (arch.num_heads * arch.full_head_dim) as u64;
let inter = arch.intermediate_size as u64;
let vocab = arch.vocab_size as u64;
let qkv_dim = qwen_shape.qkv_dim() as u64;
let value_dim = qwen_shape.value_dim() as u64;
let v_heads = qwen_shape.num_v_heads as u64;
let b = batch as u64;
let wide_blobs = if arch.num_experts == 0 {
None
} else {
Some(gpu::RoutedBlobsWideBuffer::new(
context,
arch.hidden_activation.contains("silu"),
)?)
};
let halfs = |n: u64| context.new_output_buffer(n.max(1) * b * 2);
let verify_tape = if record_tape {
let linear_layers = (0..arch.num_layers as usize)
.filter(|&l| arch.layer_is_linear(l))
.count() as u64;
Some(BatchedVerifyTape {
qkv_raw: halfs(linear_layers * qkv_dim),
a: halfs(linear_layers * v_heads),
b: halfs(linear_layers * v_heads),
})
} else {
None
};
let routed = wide_blobs.map(|wide_blobs| {
let top_k = arch.top_k_experts as u64;
let moe_inter = arch.moe_intermediate_size.max(1) as u64;
BatchedRoutedScratch {
batch_acts: halfs(top_k * moe_inter),
batch_y: halfs(hidden),
batch_h1: halfs(hidden),
batch_gate_logit: halfs(1),
batch_routing_w: halfs(top_k),
batch_routes: context
.new_output_buffer(b * top_k * gpu::MoePrefillRoute::STRIDE_BYTES as u64),
batch_router_logits_f32: context
.new_output_buffer(b * arch.num_experts.max(1) as u64 * 4),
wide_blobs,
}
});
Ok(Self {
batch,
normed: halfs(hidden),
q_packed: halfs(2 * q_dim),
q: halfs(q_dim),
attn_gate: halfs(q_dim),
attn_out: halfs(q_dim),
o: halfs(hidden),
gdn_qkv_raw: halfs(qkv_dim),
gdn_conv_out: halfs(qkv_dim),
gdn_z: halfs(value_dim),
gdn_a: halfs(v_heads),
gdn_b: halfs(v_heads),
gdn_y: halfs(value_dim),
gdn_out: halfs(value_dim),
moe_x: halfs(hidden),
ffn_gate: halfs(inter),
ffn_up: halfs(inter),
ffn_act: halfs(inter),
h2: halfs(hidden),
logits: context.new_output_buffer(vocab * if record_tape { b } else { 1 } * 2),
routed,
verify_tape,
steel_gemm: false,
rows_attention: None,
})
}
}