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