1use anyhow::Result;
4use spark_runtime::gpu::{DevicePtr, GpuBackend};
5use spark_runtime::kv_cache::PagedKvCache;
6
7use super::Qwen3AttentionLayer;
8use crate::layer::{
9 BatchedAttnMetadata, EmptyLayerState, ForwardContext, LayerState, TransformerLayer,
10};
11use crate::layers::FfnComponent;
12
13mod decode_inner;
14mod multi_seq;
15mod prefill_inner;
16
17pub(super) fn diag_norm(
19 gpu: &dyn GpuBackend,
20 ptr: DevicePtr,
21 n_elements: usize,
22 stream: u64,
23 label: &str,
24) {
25 let _ = gpu.synchronize(stream);
26 let mut buf = vec![0u16; n_elements];
27 let bytes =
33 unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, n_elements * 2) };
34 if gpu.copy_d2h(ptr, bytes).is_err() {
35 return;
36 }
37 let vals: Vec<f32> = buf
38 .iter()
39 .map(|&b| f32::from_bits((b as u32) << 16))
40 .collect();
41 let norm: f32 = vals.iter().map(|v| v * v).sum::<f32>().sqrt();
42 let max_abs: f32 = vals.iter().map(|v| v.abs()).fold(0.0f32, f32::max);
43 let f4 = if vals.len() >= 4 {
44 format!(
45 "[{:.4},{:.4},{:.4},{:.4}]",
46 vals[0], vals[1], vals[2], vals[3]
47 )
48 } else {
49 format!("{:?}", &vals[..vals.len().min(4)])
50 };
51 tracing::info!("DIAG {label}: norm={norm:.4} max={max_abs:.4} first4={f4} n={n_elements}");
52}
53
54pub fn diag_norm_f32(
58 gpu: &dyn GpuBackend,
59 ptr: DevicePtr,
60 n_elements: usize,
61 stream: u64,
62 label: &str,
63) {
64 let _ = gpu.synchronize(stream);
65 let mut buf = vec![0f32; n_elements];
66 let bytes =
72 unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, n_elements * 4) };
73 if gpu.copy_d2h(ptr, bytes).is_err() {
74 return;
75 }
76 let norm: f32 = buf.iter().map(|v| v * v).sum::<f32>().sqrt();
77 let max_abs: f32 = buf.iter().map(|v| v.abs()).fold(0.0f32, f32::max);
78 let f4 = if buf.len() >= 4 {
79 format!("[{:.4},{:.4},{:.4},{:.4}]", buf[0], buf[1], buf[2], buf[3])
80 } else {
81 format!("{:?}", &buf[..buf.len().min(4)])
82 };
83 tracing::info!(
84 "DIAG {label}: norm={norm:.4} max={max_abs:.4} first4={f4} n={n_elements} (FP32)"
85 );
86}
87
88impl TransformerLayer for Qwen3AttentionLayer {
93 fn uses_local_mla_prefill(&self) -> bool {
94 self.mla.is_some()
95 }
96
97 fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
98 Some(self)
99 }
100
101 fn fp8_calibration_frozen(&self) -> Option<bool> {
102 self.fp8_calibration
103 .as_ref()
104 .map(|cal| !cal.is_calibrating())
105 }
106
107 fn decode_graph_unsupported(&self) -> bool {
111 self.qsa.is_some()
112 }
113
114 fn has_aux_state(&self) -> bool {
115 self.qsa.is_some()
116 }
117
118 fn snapshot_aux(
119 &self,
120 state: &dyn LayerState,
121 gpu: &dyn GpuBackend,
122 stream: u64,
123 ) -> Result<Option<Vec<u8>>> {
124 let Some(qsa) = self.qsa.as_ref() else {
125 return Ok(None);
126 };
127 let attn = state
128 .as_any()
129 .downcast_ref::<crate::layer::AttnLayerState>()
130 .ok_or_else(|| anyhow::anyhow!("QSA host layer state is not AttnLayerState"))?;
131 match attn.qsa.as_ref() {
132 Some(st) => Ok(Some(qsa.snapshot_aux(st, gpu, stream)?)),
133 None => Ok(None),
135 }
136 }
137
138 fn restore_aux(
139 &self,
140 state: &mut dyn LayerState,
141 blob: &[u8],
142 gpu: &dyn GpuBackend,
143 stream: u64,
144 ) -> Result<()> {
145 let qsa = self
146 .qsa
147 .as_ref()
148 .ok_or_else(|| anyhow::anyhow!("restore_aux: no QSA on this layer"))?;
149 let attn = state
150 .as_any_mut()
151 .downcast_mut::<crate::layer::AttnLayerState>()
152 .ok_or_else(|| anyhow::anyhow!("QSA host layer state is not AttnLayerState"))?;
153 if attn.qsa.is_none() {
154 attn.qsa = Some(qsa.new_seq_state(gpu)?);
155 }
156 qsa.restore_aux(attn.qsa.as_mut().expect("just created"), blob, gpu, stream)
157 }
158
159 fn decode(
160 &self,
161 hidden: DevicePtr,
162 residual: DevicePtr,
163 state: &mut dyn LayerState,
164 kv_cache: &mut PagedKvCache,
165 seq_len: usize,
166 block_table: &mut Vec<u32>,
167 disk_block_ids: &mut Vec<u32>,
168 disk_last_offloaded_per_layer: &mut Vec<u32>,
169 ctx: &ForwardContext,
170 stream: u64,
171 ) -> Result<()> {
172 self.decode_inner(
173 hidden,
174 residual,
175 state,
176 kv_cache,
177 seq_len,
178 block_table,
179 disk_block_ids,
180 disk_last_offloaded_per_layer,
181 ctx,
182 stream,
183 )
184 }
185
186 #[allow(clippy::too_many_arguments)]
187 fn prefill(
188 &self,
189 hidden: DevicePtr,
190 residual: DevicePtr,
191 num_tokens: usize,
192 state: &mut dyn LayerState,
193 kv_cache: &mut PagedKvCache,
194 seq_len_start: usize,
195 block_table: &mut Vec<u32>,
196 disk_block_ids: &mut Vec<u32>,
197 disk_last_offloaded_per_layer: &mut Vec<u32>,
198 kv_write_start: usize,
199 ctx: &ForwardContext,
200 stream: u64,
201 ) -> Result<()> {
202 self.prefill_inner(
203 hidden,
204 residual,
205 num_tokens,
206 state,
207 kv_cache,
208 seq_len_start,
209 block_table,
210 disk_block_ids,
211 disk_last_offloaded_per_layer,
212 kv_write_start,
213 None, ctx,
215 stream,
216 )
217 }
218
219 fn prefill_inner_batched_q12(
225 &self,
226 hidden_stacked: DevicePtr,
227 residual_stacked: DevicePtr,
228 num_tokens: usize,
229 kv_cache: &mut PagedKvCache,
230 seq_len_start: usize,
231 batched_meta: &BatchedAttnMetadata,
232 ctx: &ForwardContext,
233 stream: u64,
234 ) -> Result<()> {
235 let mut empty_state = EmptyLayerState;
236 let mut empty_block_table: Vec<u32> = Vec::new();
237 let mut empty_disk_block_ids: Vec<u32> = Vec::new();
238 let mut empty_disk_last: Vec<u32> = Vec::new();
239 self.prefill_inner(
240 hidden_stacked,
241 residual_stacked,
242 num_tokens,
243 &mut empty_state,
244 kv_cache,
245 seq_len_start,
246 &mut empty_block_table,
247 &mut empty_disk_block_ids,
248 &mut empty_disk_last,
249 0,
250 Some(batched_meta),
251 ctx,
252 stream,
253 )
254 }
255
256 #[allow(clippy::too_many_arguments)]
257 fn decode_multi_seq<'a, 'b: 'a>(
258 &self,
259 hidden: DevicePtr,
260 residual: DevicePtr,
261 num_seqs: usize,
262 states: &'a mut [&'b mut (dyn LayerState + 'static)],
263 kv_cache: &mut PagedKvCache,
264 seq_lens: &[usize],
265 block_tables: &[Vec<u32>],
266 ctx: &ForwardContext,
267 stream: u64,
268 ) -> Result<()> {
269 self.decode_multi_seq_inner(
270 hidden,
271 residual,
272 num_seqs,
273 states,
274 kv_cache,
275 seq_lens,
276 block_tables,
277 ctx,
278 stream,
279 )
280 }
281
282 fn alloc_state(&self, _gpu: &dyn GpuBackend) -> Result<Box<dyn LayerState>> {
283 Ok(Box::new(crate::layer::AttnLayerState::default()))
284 }
285
286 fn release_state(&self, state: &mut dyn LayerState, gpu: &dyn GpuBackend) -> Result<()> {
297 let Some(attn) = state
298 .as_any_mut()
299 .downcast_mut::<crate::layer::AttnLayerState>()
300 else {
301 return Ok(());
302 };
303 let Some(mut st) = attn.qsa.take() else {
304 return Ok(());
305 };
306 let Some(qsa) = self.qsa.as_ref() else {
307 anyhow::bail!("release_state: QSA seq state present but layer has no QSA indexer");
310 };
311 qsa.release_seq_state(&mut st, gpu)
312 }
313
314 fn transpose_moe_for_prefill(
315 &mut self,
316 gpu: &dyn GpuBackend,
317 config: &atlas_core::config::ModelConfig,
318 ) -> Result<()> {
319 if let FfnComponent::Moe(moe) = &mut self.ffn {
320 moe.transpose_for_prefill(gpu, config)?;
321 }
322 if let Some(FfnComponent::Moe(moe)) = self.moe_ffn.as_mut() {
323 moe.transpose_for_prefill(gpu, config)?;
324 }
325 Ok(())
326 }
327
328 fn transpose_moe_gate_up_for_prefill(
329 &mut self,
330 gpu: &dyn GpuBackend,
331 config: &atlas_core::config::ModelConfig,
332 ) -> Result<()> {
333 if let FfnComponent::Moe(moe) = &mut self.ffn {
334 moe.transpose_gate_up_for_prefill(gpu, config)?;
335 }
336 if let Some(FfnComponent::Moe(moe)) = self.moe_ffn.as_mut() {
337 moe.transpose_gate_up_for_prefill(gpu, config)?;
338 }
339 Ok(())
340 }
341
342 fn set_moe_down_transpose_scratch(
343 &mut self,
344 scratch_packed: DevicePtr,
345 scratch_scale: DevicePtr,
346 packed_ptrs_t: DevicePtr,
347 scale_ptrs_t: DevicePtr,
348 ) {
349 if let FfnComponent::Moe(moe) = &mut self.ffn {
350 moe.set_down_transpose_scratch(
351 scratch_packed,
352 scratch_scale,
353 packed_ptrs_t,
354 scale_ptrs_t,
355 );
356 }
357 if let Some(FfnComponent::Moe(moe)) = self.moe_ffn.as_mut() {
358 moe.set_down_transpose_scratch(
359 scratch_packed,
360 scratch_scale,
361 packed_ptrs_t,
362 scale_ptrs_t,
363 );
364 }
365 }
366
367 fn transpose_moe_for_prefill_unified(
368 &mut self,
369 gpu: &dyn GpuBackend,
370 config: &atlas_core::config::ModelConfig,
371 ) -> Result<()> {
372 if let FfnComponent::Moe(moe) = &mut self.ffn {
373 moe.transpose_for_prefill_unified(gpu, config)?;
374 }
375 if let Some(FfnComponent::Moe(moe)) = self.moe_ffn.as_mut() {
376 moe.transpose_for_prefill_unified(gpu, config)?;
377 }
378 Ok(())
379 }
380
381 fn transpose_moe_for_prefill_hybrid(
382 &mut self,
383 gpu: &dyn GpuBackend,
384 config: &atlas_core::config::ModelConfig,
385 ) -> Result<()> {
386 if let FfnComponent::Moe(moe) = &mut self.ffn {
387 moe.transpose_for_prefill_hybrid(gpu, config)?;
388 }
389 if let Some(FfnComponent::Moe(moe)) = self.moe_ffn.as_mut() {
390 moe.transpose_for_prefill_hybrid(gpu, config)?;
391 }
392 Ok(())
393 }
394}