span-02 / engine /crates /model /src /gpu_model /forward.rs
lihe07's picture
span-02: single-source release folder (engine final path, soft blend)
19886eb verified
Raw History Blame Contribute Delete
8.3 kB
//! The decoder forward over one row batch (child module of `gpu_model`: sees the private fields): embedding + soft injection -> 30 layers -> final norm -> `hidden`.
//! Per layer: fused norm + fp8 quant producers, FP8 MMA GEMV (<= 16 rows) / GEMM, mma attention, NVFP4 W4A4 block-scaled MMA MoE. In decode steps the dense
//! MLP runs on a second stream, overlapping the router + MoE chain.
use super::*;
use dllm_kernels::attn as fattn;
use dllm_kernels::linear as flin;
use dllm_kernels::moe::MoeDecodeScratch;
impl GpuModel {
/// host-side prep of a prefill forward: attention tiles (consecutive rows sharing a block table)
pub(super) fn prepare_tiles(&self, bt_off: &[i32], decode: bool) -> Result<()> {
if decode {
return Ok(());
}
let ws = &self.ws;
for (i, hd) in [256usize, 512].iter().enumerate() {
let (r0, n) = fattn::make_tiles(bt_off, fattn::prefill_rows_per_tile(*hd));
ws.tile_row0[i].upload_slice(&r0)?;
ws.tile_n[i].upload_slice(&n)?;
ws.ntiles[i].store(r0.len(), Ordering::Relaxed);
}
Ok(())
}
pub(super) fn forward_rows(&self, r: usize, decode: bool, splits: usize, m: DevMeta) -> Result<()> {
let gpu = self.gpu.clone();
let be = &gpu.be;
let cfg = &self.cfg;
let (h, eps, dff) = (cfg.hidden_size, cfg.rms_eps, cfg.intermediate_size);
let (k_top, im, ne) = (cfg.top_k_experts, cfg.moe_intermediate_size, cfg.num_experts);
let ws = &self.ws;
gpu.embed_gather_fp8(ws.x.ptr, self.embed_w.ptr, self.embed_s.ptr, m.tokens, r, h, self.embed_scale)?;
let n_soft = self.soft_n.swap(0, Ordering::Relaxed);
if n_soft > 0 {
gpu.inject_soft(ws.x.ptr, self.soft_src.load(Ordering::Relaxed), self.soft_idx.ptr, self.soft_idx.at(self.opts.max_rows * 4), n_soft, h)?;
}
let small = r <= 16;
let (aq, a_s) = (ws.aq.ptr, ws.a_s.ptr);
flin::quant_rows(be, aq, a_s, ws.x.ptr, self.layers[0].in_ln.ptr, eps, r, h, true)?;
let nl = self.layers.len();
for (i, l) in self.layers.iter().enumerate() {
let sp = &l.spec;
let moe = &l.moe;
let fe = &moe.experts;
let (nh, nkv, hd) = (cfg.num_heads, sp.n_kv_heads, sp.head_dim);
let (nq, nk) = (nh * hd, nkv * hd);
let nv = if l.v.is_some() { nk } else { 0 };
let hi = if sp.kind == LayerKind::Sliding { m.kv_hi_sl } else { m.kv_hi };
let window = if sp.kind == LayerKind::Sliding { sp.window.unwrap_or(0) } else { 0 };
let pm = self.kv_pmap(i);
// ---- qkv
if small {
let ld = nq + nk + nv;
let mut segs = vec![l.q.seg(), l.k.seg()];
if let Some(v) = &l.v {
segs.push(v.seg());
}
flin::gemv_pqd(be, ws.qkv.ptr, ld, aq, a_s, r, h, &segs, false, None)?;
let vr = if l.v.is_some() { ws.qkv.at((nq + nk) * 2) } else { ws.qkv.at(nq * 2) };
fattn::qkv_prep_ld(&gpu, ws.q.ptr, self.kpool[i].ptr, self.vpool[i].ptr, ws.qkv.ptr, ws.qkv.at(nq * 2), vr, l.q_norm.ptr, l.k_norm.ptr, self.rope[l.rope].ptr, m.pos, m.slot, r, nh, nkv, hd, eps, ld, ld, ld, pm)?;
} else {
let (q, k) = (l.q.seg(), l.k.seg());
flin::gemm(be, ws.qraw.ptr, aq, a_s, q.w, q.s, r, nq, h)?;
flin::gemm(be, ws.kraw.ptr, aq, a_s, k.w, k.s, r, nk, h)?;
let vsrc = if let Some(v) = &l.v {
let v = v.seg();
flin::gemm(be, ws.vraw.ptr, aq, a_s, v.w, v.s, r, nv, h)?;
ws.vraw.ptr
} else {
ws.kraw.ptr
};
fattn::qkv_prep(&gpu, ws.q.ptr, self.kpool[i].ptr, self.vpool[i].ptr, ws.qraw.ptr, ws.kraw.ptr, vsrc, l.q_norm.ptr, l.k_norm.ptr, self.rope[l.rope].ptr, m.pos, m.slot, r, nh, nkv, hd, eps, pm)?;
}
// ---- attention
if decode {
let sp_n = fattn::choose_splits(splits.max(1) * 1024, r, nkv, if r == 1 { 1 } else { 5 });
let (part, cnt) = if sp_n > 1 { (ws.part.ptr, ws.attn_cnt.ptr) } else { (0, 0) };
fattn::attn_decode(&gpu, ws.ao.ptr, part, ws.q.ptr, self.kpool[i].ptr, self.vpool[i].ptr, m.bt, m.bt_off, m.pos, hi, r, nh, nkv, hd, window, sp_n, cnt, pm)?;
} else {
let ti = if hd == 512 { 1 } else { 0 };
let nt = ws.ntiles[ti].load(Ordering::Relaxed);
ensure!(nt > 0, "prefill needs the host tile table (call GpuModel::forward, not forward_dev, for prefill batches)");
fattn::attn_prefill(&gpu, ws.ao.ptr, ws.q.ptr, self.kpool[i].ptr, self.vpool[i].ptr, m.bt, m.bt_off, m.pos, hi, ws.tile_row0[ti].ptr, ws.tile_n[ti].ptr, nt, nh, nkv, hd, window, pm)?;
}
// ---- o_proj
flin::quant_rows(be, aq, a_s, ws.ao.ptr, 0, eps, r, nq, false)?;
let o = l.o.seg();
if small {
flin::gemv_pqd(be, ws.o.ptr, h, aq, a_s, r, nq, &[o], false, None)?;
} else {
flin::gemm(be, ws.o.ptr, aq, a_s, o.w, o.s, r, h, nq)?;
}
// ---- sandwich + producers of the dense / expert / router inputs
gpu.post_attn(ws.mid.ptr, ws.fq.ptr, ws.fs.ptr, ws.mxq.ptr, ws.mxs.ptr, ws.rn.ptr, ws.x.ptr, ws.o.ptr, l.post_attn_ln.ptr, l.pre_ff_ln.ptr, moe.pre_ff_ln2.ptr, moe.router_scale.ptr, fe.act_g, r, h, eps)?;
// ---- dense MLP (own stream branch in decode: overlaps the router + MoE chain)
if small {
be.branch_fork()?;
be.branch_use(true);
}
let (g, u, d) = (l.gate.seg(), l.up.seg(), l.down.seg());
if small {
flin::gemv_pqd(be, ws.act.ptr, dff, ws.fq.ptr, ws.fs.ptr, r, h, &[g, u], true, None)?;
flin::quant_rows(be, aq, a_s, ws.act.ptr, 0, eps, r, dff, false)?;
flin::gemv_pqd(be, ws.m.ptr, h, aq, a_s, r, dff, &[d], false, None)?;
} else {
flin::gemm_glu(be, ws.act.ptr, ws.fq.ptr, ws.fs.ptr, g.w, g.s, u.w, u.s, r, dff, h)?;
flin::quant_rows(be, aq, a_s, ws.act.ptr, 0, eps, r, dff, false)?;
flin::gemm(be, ws.m.ptr, aq, a_s, d.w, d.s, r, h, dff)?;
}
if small {
be.branch_use(false);
}
// ---- MoE
let proj = moe.router_proj.ptr;
if small {
let s = MoeDecodeScratch { logits: ws.rlog.ptr, ids: ws.topk_ids.ptr, wts: ws.topk_w.ptr, ticket: ws.ticket.ptr, hq: ws.mhq.ptr, hs: ws.mhs.ptr, opair: ws.moe_o.ptr, grp: ws.grp.ptr };
gpu.moe_router_decode(&s, ws.rn.ptr, proj, moe.per_expert_scale.ptr, r, h)?;
gpu.moe_glu_decode(fe, &s, ws.mxq.ptr, ws.mxs.ptr, r, h, im)?;
gpu.moe_down_decode(fe, &s, r, h, im)?;
} else {
gpu.moe_router_logits_prefill(ws.rlog.ptr, ws.rn.ptr, proj, r, h)?;
gpu.moe_topk_prefill(ws.topk_ids.ptr, ws.topk_w.ptr, ws.rlog.ptr, moe.per_expert_scale.ptr, r)?;
gpu.moe_route(ws.counts.ptr, ws.offsets.ptr, ws.cursor.ptr, ws.pair_of_slot.ptr, ws.slot_of_pair.ptr, ws.topk_ids.ptr, r, k_top, ne)?;
gpu.moe_glu_prefill(fe, ws.mhq.ptr, ws.mhs.ptr, ws.mxq.ptr, ws.mxs.ptr, ws.pair_of_slot.ptr, ws.offsets.ptr, ws.counts.ptr, r, h, im, ne)?;
gpu.moe_down_prefill(fe, ws.moe_o.ptr, ws.mhq.ptr, ws.mhs.ptr, ws.pair_of_slot.ptr, ws.offsets.ptr, ws.counts.ptr, r, h, im, ne)?;
}
if small {
be.branch_join()?;
}
// ---- combine + layer end (+ next layer's input norm / fp8 quant)
let next = if i + 1 < nl { Some((self.layers[i + 1].in_ln.ptr, aq, a_s)) } else { None };
gpu.layer_final(ws.x.ptr, ws.m.ptr, ws.moe_o.ptr, ws.topk_w.ptr, ws.mid.ptr, l.post_ff_ln1.ptr, moe.post_ff_ln2.ptr, l.post_ff_ln.ptr, l.layer_scalar.ptr, r, h, eps, next)?;
}
gpu.rmsnorm(ws.hidden.ptr, ws.x.ptr, self.final_norm.ptr, r, h, eps)?;
Ok(())
}
}