metrale_model_layers/layers/
qsa_snapshot.rs1use anyhow::Result;
12use metrale_gpu_runtime::gpu::GpuBackend;
13
14use super::{QsaIndexer, QsaSeqState};
15use crate::layers::ops;
16
17impl QsaIndexer {
18 pub fn snapshot_aux(
22 &self,
23 st: &QsaSeqState,
24 gpu: &dyn GpuBackend,
25 stream: u64,
26 ) -> Result<Vec<u8>> {
27 let hd = self.hd as usize;
28 let key_bytes = st.ingested * hd * 2;
29 let mut blob = Vec::with_capacity(16 + key_bytes);
30 blob.extend_from_slice(&(st.ingested as u64).to_le_bytes());
31 blob.extend_from_slice(&(st.pooled as u64).to_le_bytes());
32 let off = blob.len();
33 blob.resize(off + key_bytes, 0);
34 if key_bytes > 0 {
35 gpu.copy_d2h_on_stream(st.raw_keys, &mut blob[off..], stream)?;
36 }
37 Ok(blob)
38 }
39
40 pub fn restore_aux(
43 &self,
44 st: &mut QsaSeqState,
45 blob: &[u8],
46 gpu: &dyn GpuBackend,
47 stream: u64,
48 ) -> Result<()> {
49 anyhow::ensure!(blob.len() >= 16, "QSA aux blob truncated");
50 let ingested = u64::from_le_bytes(blob[..8].try_into().unwrap()) as usize;
51 let pooled = u64::from_le_bytes(blob[8..16].try_into().unwrap()) as usize;
52 let hd = self.hd as usize;
53 anyhow::ensure!(
54 blob.len() == 16 + ingested * hd * 2,
55 "QSA aux blob size mismatch"
56 );
57 anyhow::ensure!(ingested <= self.max_tokens, "QSA aux exceeds key cache");
58 if ingested > 0 {
59 gpu.copy_h2d_async(&blob[16..], st.raw_keys, stream)?;
60 }
61 st.ingested = ingested;
62 st.pooled = 0;
63 if pooled > 0 {
64 ops::qsa_block_pool(
65 gpu,
66 self.k_pool_k,
67 st.raw_keys,
68 self.k_norm_w,
69 st.block_keys,
70 0,
71 pooled as u32,
72 self.ratio,
73 self.hd,
74 self.rot,
75 self.theta,
76 self.eps,
77 stream,
78 )?;
79 st.pooled = pooled;
80 }
81 Ok(())
82 }
83}