metrale_model_layers/layers/ops/w8a8_decode/parts.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-30: The parts of a W8A8 projection the circuit executor launches one group at a
4//! time: a stacked weight's segments, the kernel each launch takes, and a scratch view over
5//! one activation buffer. The launches themselves stay [`super::w8a8_act_quant`],
6//! [`super::w8a8_act_quant_silu`] and [`super::w8a8_gemv`].
7//!
8//! Owner: model-layers ops.
9//! Invariants:
10//! - [`W8a8Kernels::gemv_entry`] is the entry [`super::w8a8_gemv`] launches for a launch of
11//! that many rows; [`W8a8Kernels::quant`] and [`W8a8Kernels::quant_silu`] are the ones the
12//! quantizers launch.
13
14use anyhow::{Result, ensure};
15use metrale_gpu_runtime::gpu::{DevicePtr, KernelHandle};
16
17use super::{W8A8_LAUNCH_ROWS, W8a8Kernels, W8a8Scale, W8a8Scratch, W8a8Weight, entry_index};
18
19impl W8a8Weight {
20 /// 2026-09-30: Segment `i` as a weight of its own. A segment's output rows read only its
21 /// own weight rows and scales, so its launch writes the bits the stacked launch writes
22 /// for those rows.
23 pub fn segment(&self, i: usize) -> Result<W8a8Weight> {
24 ensure!(i < self.count, "W8a8Weight: segment {i} of {}", self.count);
25 W8a8Weight::new(&[self.segs[i]])
26 }
27
28 /// 2026-09-30: Stacked segments.
29 pub fn segments(&self) -> usize {
30 self.count
31 }
32}
33
34impl W8a8Kernels {
35 /// 2026-09-30: The quantizer of `scale`'s layout.
36 pub fn quant(&self, scale: W8a8Scale) -> KernelHandle {
37 match scale {
38 W8a8Scale::PerRow => self.quant_row,
39 W8a8Scale::Block128 => self.quant_g128,
40 }
41 }
42
43 /// 2026-09-30: The SiLU·mul quantizer of `scale`'s layout.
44 pub fn quant_silu(&self, scale: W8a8Scale) -> KernelHandle {
45 match scale {
46 W8a8Scale::PerRow => self.quant_silu_row,
47 W8a8Scale::Block128 => self.quant_silu_g128,
48 }
49 }
50
51 /// 2026-09-30: The GEMV entry of one launch of `rows` rows; `None` above one launch.
52 pub fn gemv_entry(&self, scale: W8a8Scale, rows: usize) -> Option<KernelHandle> {
53 if !(1..=W8A8_LAUNCH_ROWS).contains(&rows) {
54 return None;
55 }
56 let e = entry_index(rows);
57 Some(match scale {
58 W8a8Scale::PerRow => self.rowscale[e],
59 W8a8Scale::Block128 => self.blk128[e],
60 })
61 }
62}
63
64impl W8a8Scratch {
65 /// 2026-09-30: One activation of `rows` rows packed from `base`: `[rows, k]` E4M3, then
66 /// its FP32 scales (the circuit's `fp8/token` edge, `Format::bytes`).
67 pub fn packed(base: DevicePtr, rows: usize, k: u32, scale: W8a8Scale) -> Self {
68 let q_bytes = rows * k as usize;
69 Self {
70 q: base,
71 q_bytes,
72 scale: base.offset(q_bytes),
73 scale_bytes: rows * scale.act_scales_per_row(k) * 4,
74 }
75 }
76}