metrale_model_arch/glm5next_dsa/layer/
rows.rs1use anyhow::{Result, bail};
12use metrale_cache::kv_cache::PagedKvCache;
13use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend, KernelHandle};
14use metrale_gpu_runtime::kernel_args::KernelLaunch;
15use metrale_model_layers::layer::{ForwardContext, LayerState};
16
17use super::super::select::{DsaSelectInputs, select_tokens};
18use super::super::state::Glm5NextDsaState;
19use super::{Glm5NextDsaLayer, gemm};
20
21impl Glm5NextDsaLayer {
22 pub(super) fn store_indexer_row(
26 &self,
27 gpu: &dyn GpuBackend,
28 state: &mut Glm5NextDsaState,
29 pos_dev: Option<DevicePtr>,
30 pos: usize,
31 d: usize,
32 stream: u64,
33 ) -> Result<()> {
34 let w = &self.workspace;
35 match pos_dev {
36 Some(pd) => {
39 KernelLaunch::new(gpu, self.select_kernels.indexer_store)
40 .grid([1, 1, 1])
41 .block([d.min(1024) as u32, 1, 1])
42 .arg_ptr(w.stage_k)
43 .arg_ptr(w.stage_gate)
44 .arg_ptr(pd)
45 .arg_ptr(state.k_normed)
46 .arg_ptr(state.gate)
47 .arg_ptr(state.valid)
48 .arg_u32(d as u32)
49 .launch(stream)?;
50 }
51 None => gpu.memset_async(state.valid.offset(pos), 1, 1, stream)?,
52 }
53 state.advance(1)
54 }
55
56 #[allow(clippy::too_many_arguments)]
64 pub(super) fn select_rows_batched(
65 &self,
66 gpu: &dyn GpuBackend,
67 k: usize,
68 state: &Glm5NextDsaState,
69 q_pos_host: &[i32],
70 stream: u64,
71 ) -> Result<()> {
72 let w = &self.workspace;
73 let geom = state.geometry(&self.cfg, k)?;
76 let idx_row = self.cfg.index_heads * self.cfg.index_head_dim;
77 if crate::glm5next_layer::dsa_batch_qidx() {
83 metrale_model_layers::layers::ops::cublas_bf16_proj_dense_f32_out(
84 w.q_resid,
85 self.weights.wq_b,
86 w.q_idx_rows,
87 k as u32,
88 idx_row as u32,
89 self.cfg.q_lora_rank as u32,
90 stream,
91 )?;
92 } else {
93 for row in 0..k {
94 gemm(
95 gpu,
96 self.kernels.gemm_f32,
97 self.kernels.gemv_f32,
98 KernelHandle(0),
100 w.q_resid.offset(row * self.cfg.q_lora_rank * 2),
101 self.weights.wq_b,
102 w.q_idx_rows.offset(row * idx_row * 4),
103 1,
104 idx_row,
105 self.cfg.q_lora_rank,
106 stream,
107 )?;
108 }
109 }
110 let q_pos_bytes: Vec<u8> = q_pos_host.iter().flat_map(|p| p.to_le_bytes()).collect();
111 gpu.copy_h2d(&q_pos_bytes, w.q_pos_rows)?;
112 let inputs = DsaSelectInputs {
113 k_normed: state.k_normed,
114 gate: state.gate,
115 valid: state.valid,
116 ape: self.weights.ape,
117 q: w.q_idx_rows,
118 weights: w.head_weights_rows,
119 q_pos: w.q_pos_rows,
120 q_mask: w.q_mask_rows,
121 first_key: 0,
122 geom_dev: DevicePtr::NULL,
125 };
126 let t = crate::glm5next_layer::profile::start();
127 select_tokens(
130 gpu,
131 &self.select_kernels,
132 &self.cfg,
133 &geom,
134 &inputs,
135 &w.select,
136 super::super::select::DsaSelectLaunch::Exact,
137 stream,
138 )?;
139 crate::glm5next_layer::profile::end(
140 crate::glm5next_layer::profile::DSA_SELECT,
141 t,
142 gpu,
143 stream,
144 );
145 Ok(())
146 }
147
148 #[allow(clippy::too_many_arguments)]
155 pub fn write_kv_row(
156 &self,
157 hidden: DevicePtr,
158 state: &mut dyn LayerState,
159 kv_cache: &mut PagedKvCache,
160 seq_len: usize,
161 block_table: &mut Vec<u32>,
162 ctx: &ForwardContext,
163 stream: u64,
164 ) -> Result<()> {
165 let gpu = ctx.gpu;
166 let st = state
167 .as_any_mut()
168 .downcast_mut::<Glm5NextDsaState>()
169 .ok_or_else(|| {
170 anyhow::anyhow!("Glm5NextDsaLayer got a state that is not Glm5NextDsaState")
171 })?;
172 match st.len().cmp(&seq_len) {
173 std::cmp::Ordering::Greater => st.rewind_to(seq_len)?,
174 std::cmp::Ordering::Less => bail!(
175 "DSA layer {}: indexer cache holds {} rows but the drafter is at {seq_len} — \
176 rows are MISSING, not merely stale.",
177 self.layer_idx,
178 st.len()
179 ),
180 std::cmp::Ordering::Equal => {}
181 }
182 let w = &self.workspace;
183 gemm(
184 gpu,
185 self.kernels.gemm,
186 self.kernels.gemv,
187 self.kernels.gemv_batchm,
188 hidden,
189 self.weights.kv_a_proj,
190 w.kv_a,
191 1,
192 self.cfg.kv_lora_rank,
193 self.cfg.hidden,
194 stream,
195 )?;
196 let block_size = kv_cache.config().block_size;
197 let logical = seq_len / block_size;
198 let physical = *block_table.get(logical).ok_or_else(|| {
199 anyhow::anyhow!(
200 "DSA layer {}: block table has {} entries, needs logical block {logical} for \
201 drafter row {seq_len}",
202 self.layer_idx,
203 block_table.len()
204 )
205 })? as usize;
206 let slot = (physical * block_size + seq_len % block_size) as i64;
207 gpu.copy_h2d(&slot.to_le_bytes(), w.slot)?;
208 KernelLaunch::new(gpu, self.kernels.latent_write)
209 .grid([1, 1, 1])
210 .block([self.cfg.kv_lora_rank as u32, 1, 1])
211 .arg_ptr(w.kv_a)
212 .arg_ptr(self.weights.kv_a_layernorm)
213 .arg_ptr(kv_cache.k_pool_ptr(self.attn_layer_idx))
214 .arg_ptr(w.slot)
215 .arg_u32(self.cfg.kv_lora_rank as u32)
216 .arg_f32(self.rms_eps)
217 .arg_f32(1.0 / self.kv_scale)
218 .launch(stream)?;
219 self.indexer_forward(gpu, hidden, st, None, stream)
220 }
221}