1use crate::compile_cache::pad_rows;
8
9#[derive(Debug, Clone)]
11pub struct LayerKvCache {
12 pub past_len: usize,
13 pub layers_k: Vec<Vec<f32>>,
14 pub layers_v: Vec<Vec<f32>>,
15 pub layers_kv_base: Vec<usize>,
19}
20
21impl LayerKvCache {
22 pub fn from_layer_outputs(
23 num_layers: usize,
24 batch: usize,
25 past_seq: usize,
26 kv_dim: usize,
27 outputs: &[Vec<f32>],
28 ) -> Result<Self, String> {
29 let dims: Vec<usize> = vec![kv_dim; num_layers];
30 Self::from_layer_outputs_per_layer(num_layers, batch, past_seq, &dims, outputs)
31 }
32
33 pub fn from_layer_outputs_per_layer(
38 num_layers: usize,
39 batch: usize,
40 past_seq: usize,
41 kv_dims: &[usize],
42 outputs: &[Vec<f32>],
43 ) -> Result<Self, String> {
44 if outputs.len() != 2 * num_layers {
45 return Err(format!(
46 "from_layer_outputs_per_layer: expected {} K/V tensors, got {}",
47 2 * num_layers,
48 outputs.len()
49 ));
50 }
51 if kv_dims.len() != num_layers {
52 return Err(format!(
53 "from_layer_outputs_per_layer: expected {} kv_dims, got {}",
54 num_layers,
55 kv_dims.len()
56 ));
57 }
58 let mut layers_k = Vec::with_capacity(num_layers);
59 let mut layers_v = Vec::with_capacity(num_layers);
60 for layer in 0..num_layers {
61 let kv_dim = kv_dims[layer];
62 let expected = batch * past_seq * kv_dim;
63 let k = &outputs[2 * layer];
64 let v = &outputs[2 * layer + 1];
65 if k.len() != expected || v.len() != expected {
66 return Err(format!(
67 "layer {layer}: k.len={} v.len={} expected {expected} (kv_dim={kv_dim})",
68 k.len(),
69 v.len()
70 ));
71 }
72 layers_k.push(k.clone());
73 layers_v.push(v.clone());
74 }
75 Ok(Self {
76 past_len: past_seq,
77 layers_k,
78 layers_v,
79 layers_kv_base: vec![0; num_layers],
80 })
81 }
82
83 pub fn pad_layers_to_upper(&self, upper: u64, kv_dim: usize) -> (Vec<Vec<f32>>, Vec<Vec<f32>>) {
85 let dims: Vec<usize> = vec![kv_dim; self.layers_k.len()];
86 self.pad_layers_to_upper_per_layer(upper, &dims)
87 }
88
89 pub fn pad_layers_to_upper_per_layer(
93 &self,
94 upper: u64,
95 kv_dims: &[usize],
96 ) -> (Vec<Vec<f32>>, Vec<Vec<f32>>) {
97 assert_eq!(
98 kv_dims.len(),
99 self.layers_k.len(),
100 "pad_layers_to_upper_per_layer: kv_dims len {} != layers {}",
101 kv_dims.len(),
102 self.layers_k.len(),
103 );
104 let padded_k = self
105 .layers_k
106 .iter()
107 .zip(kv_dims.iter())
108 .map(|(k, &d)| pad_rows(k, d, upper))
109 .collect();
110 let padded_v = self
111 .layers_v
112 .iter()
113 .zip(kv_dims.iter())
114 .map(|(v, &d)| pad_rows(v, d, upper))
115 .collect();
116 (padded_k, padded_v)
117 }
118
119 pub fn advance_from_decode_outputs(
121 &mut self,
122 outputs: Vec<Vec<f32>>,
123 batch: usize,
124 kv_dim: usize,
125 ) -> Result<(), String> {
126 let dims: Vec<usize> = vec![kv_dim; self.layers_k.len()];
127 self.advance_from_decode_outputs_per_layer(outputs, batch, &dims)
128 }
129
130 pub fn trim_sliding_window_per_layer(
149 &mut self,
150 kv_dims_keep: &[Option<(usize, usize)>],
151 ) -> Result<(), String> {
152 if kv_dims_keep.len() != self.layers_k.len() {
153 return Err(format!(
154 "trim_sliding_window_per_layer: kv_dims_keep len {} != layers {}",
155 kv_dims_keep.len(),
156 self.layers_k.len(),
157 ));
158 }
159 for (i, spec) in kv_dims_keep.iter().enumerate() {
160 let Some((kv_dim, window)) = spec else {
161 continue;
162 };
163 let kv_dim = *kv_dim;
164 let window = *window;
165 if window == 0 || kv_dim == 0 {
166 continue;
167 }
168 let rows = self.layers_k[i].len() / kv_dim;
169 if rows <= window {
170 continue;
171 }
172 let drop_rows = rows - window;
173 let drop_bytes = drop_rows * kv_dim;
174 self.layers_k[i].drain(..drop_bytes);
175 self.layers_v[i].drain(..drop_bytes);
176 if self.layers_kv_base.len() <= i {
177 self.layers_kv_base.resize(self.layers_k.len(), 0);
178 }
179 self.layers_kv_base[i] = self.layers_kv_base[i].saturating_add(drop_rows);
180 }
181 Ok(())
182 }
183
184 pub fn advance_from_decode_outputs_per_layer(
186 &mut self,
187 outputs: Vec<Vec<f32>>,
188 _batch: usize,
189 kv_dims: &[usize],
190 ) -> Result<(), String> {
191 let n = self.layers_k.len();
192 if outputs.len() != 1 + 2 * n {
193 return Err(format!(
194 "advance_from_decode_outputs_per_layer: expected {} outputs, got {}",
195 1 + 2 * n,
196 outputs.len()
197 ));
198 }
199 if kv_dims.len() != n {
200 return Err(format!(
201 "advance_from_decode_outputs_per_layer: kv_dims len {} != layers {n}",
202 kv_dims.len()
203 ));
204 }
205 let new_len = self.past_len + 1;
206 let mut iter = outputs.into_iter();
207 let _logits = iter.next().ok_or("missing logits")?;
208 for i in 0..n {
209 let k = iter.next().ok_or("missing k")?;
210 let v = iter.next().ok_or("missing v")?;
211 let real_len = new_len * kv_dims[i];
212 self.layers_k[i] = k[..real_len.min(k.len())].to_vec();
213 self.layers_v[i] = v[..real_len.min(v.len())].to_vec();
214 }
215 self.past_len = new_len;
216 Ok(())
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 #[test]
225 fn sliding_window_trim_keeps_last_w_rows() {
226 let kv_dim = 4;
228 let rows = 6;
229 let mut cache = LayerKvCache {
230 past_len: rows,
231 layers_k: vec![(0..(rows * kv_dim)).map(|x| x as f32).collect(); 3],
232 layers_v: vec![(0..(rows * kv_dim)).map(|x| x as f32).collect(); 3],
233 layers_kv_base: vec![0; 3],
234 };
235 let spec = [Some((kv_dim, 2)), None, Some((kv_dim, 4))];
237 cache.trim_sliding_window_per_layer(&spec).unwrap();
238 assert_eq!(cache.layers_k[0].len(), 2 * kv_dim);
239 assert_eq!(cache.layers_kv_base[0], 4);
240 assert_eq!(cache.layers_kv_base[1], 0);
241 assert_eq!(cache.layers_kv_base[2], 2);
242 assert_eq!(
244 cache.layers_k[0],
245 vec![16., 17., 18., 19., 20., 21., 22., 23.]
246 );
247 assert_eq!(
248 cache.layers_k[1].len(),
249 6 * kv_dim,
250 "untouched layer keeps full history"
251 );
252 assert_eq!(cache.layers_k[2].len(), 4 * kv_dim);
253 }
254
255 #[test]
256 fn sliding_window_trim_no_op_when_under_window() {
257 let kv_dim = 4;
258 let rows = 3;
259 let mut cache = LayerKvCache {
260 past_len: rows,
261 layers_k: vec![vec![1.0f32; rows * kv_dim]],
262 layers_v: vec![vec![2.0f32; rows * kv_dim]],
263 layers_kv_base: vec![0],
264 };
265 cache
266 .trim_sliding_window_per_layer(&[Some((kv_dim, 10))])
267 .unwrap();
268 assert_eq!(cache.layers_k[0].len(), rows * kv_dim);
269 }
270}