use super::KvCache;
use candle_core::{Result as CandleResult, Tensor};
#[derive(Debug, Default)]
pub struct StandardCache {
k: Option<Tensor>,
v: Option<Tensor>,
}
impl StandardCache {
pub fn new() -> Self {
Self { k: None, v: None }
}
pub fn with_kv(k: Tensor, v: Tensor) -> Self {
Self {
k: Some(k),
v: Some(v),
}
}
}
impl KvCache for StandardCache {
fn append(&mut self, k: &Tensor, v: &Tensor) -> CandleResult<()> {
let (new_k, new_v) = match (&self.k, &self.v) {
(Some(prev_k), Some(prev_v)) => {
let k_cat = Tensor::cat(&[prev_k, k], 2)?;
let v_cat = Tensor::cat(&[prev_v, v], 2)?;
(k_cat, v_cat)
},
_ => (k.clone(), v.clone()),
};
self.k = Some(new_k);
self.v = Some(new_v);
Ok(())
}
fn seq_len(&self) -> usize {
self.k
.as_ref()
.map(|k| k.dims()[2]) .unwrap_or(0)
}
fn clear(&mut self) {
self.k = None;
self.v = None;
}
fn memory_bytes(&self) -> usize {
let k_bytes = self
.k
.as_ref()
.map(|t| t.elem_count() * t.dtype().size_in_bytes())
.unwrap_or(0);
let v_bytes = self
.v
.as_ref()
.map(|t| t.elem_count() * t.dtype().size_in_bytes())
.unwrap_or(0);
k_bytes + v_bytes
}
fn get_kv(&self) -> CandleResult<Option<(Tensor, Tensor)>> {
match (&self.k, &self.v) {
(Some(k), Some(v)) => Ok(Some((k.clone(), v.clone()))),
_ => Ok(None),
}
}
fn supports_fused_attention(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use candle_core::{DType, Device};
#[test]
fn test_standard_cache_empty() {
let cache = StandardCache::new();
assert_eq!(cache.seq_len(), 0);
assert_eq!(cache.memory_bytes(), 0);
assert!(cache.get_kv().unwrap().is_none());
}
#[test]
fn test_standard_cache_append() -> CandleResult<()> {
let mut cache = StandardCache::new();
let device = Device::Cpu;
let k1 = Tensor::zeros((1, 2, 3, 64), DType::F32, &device)?;
let v1 = Tensor::zeros((1, 2, 3, 64), DType::F32, &device)?;
cache.append(&k1, &v1)?;
assert_eq!(cache.seq_len(), 3);
let k2 = Tensor::zeros((1, 2, 2, 64), DType::F32, &device)?;
let v2 = Tensor::zeros((1, 2, 2, 64), DType::F32, &device)?;
cache.append(&k2, &v2)?;
assert_eq!(cache.seq_len(), 5);
let (k, v) = cache.get_kv()?.unwrap();
assert_eq!(k.dims(), &[1, 2, 5, 64]);
assert_eq!(v.dims(), &[1, 2, 5, 64]);
Ok(())
}
#[test]
fn test_standard_cache_clear() -> CandleResult<()> {
let mut cache = StandardCache::new();
let device = Device::Cpu;
let k = Tensor::zeros((1, 2, 3, 64), DType::F32, &device)?;
let v = Tensor::zeros((1, 2, 3, 64), DType::F32, &device)?;
cache.append(&k, &v)?;
assert_eq!(cache.seq_len(), 3);
cache.clear();
assert_eq!(cache.seq_len(), 0);
assert!(cache.get_kv()?.is_none());
Ok(())
}
#[test]
fn test_standard_cache_memory() -> CandleResult<()> {
let mut cache = StandardCache::new();
let device = Device::Cpu;
let k = Tensor::zeros((1, 2, 3, 64), DType::F32, &device)?;
let v = Tensor::zeros((1, 2, 3, 64), DType::F32, &device)?;
cache.append(&k, &v)?;
assert_eq!(cache.memory_bytes(), 1 * 2 * 3 * 64 * 4 * 2);
Ok(())
}
}