Skip to main content

sva_engine/
cache.rs

1// Concern: what memory and a disk hold, and the one key they hold it under | Non-concern: what memory keeps and evicts (memory.rs) | IO: (identity, Question) -> key; (Hash) -> a payload
2
3mod codec;
4mod index;
5pub(crate) mod log;
6mod memory;
7mod persist;
8mod stats;
9mod stored;
10mod tier;
11
12pub use codec::STORE_FORMAT;
13pub use memory::{Counters, DEFAULT_CACHE_BYTES, DEFAULT_MARK_EVERY};
14pub(crate) use memory::{Facts, Keep, Known, Memory, Offered};
15pub use persist::{Backend, DEFAULT_STORE_BYTES, INDEX_NAME, Persisted, Store};
16pub(crate) use stats::Recording;
17pub use stats::{CacheStats, Lookup, Outcome};
18pub use stored::Stored;
19pub use sva_formula::Hash;
20pub(crate) use tier::now;
21pub use tier::{FETCH_READS, Nothing, Tier};
22
23use std::collections::BTreeMap;
24use std::sync::Arc;
25
26use sva_samples::{Buffer, Frames, Grid, Label, MachineState, Profile};
27
28/// A value's segments, a stateful value's run, or one analysis of a value, each shared: a clone
29/// hands out the same samples, never a copy of them.
30#[derive(Clone, Debug, PartialEq)]
31pub enum Payload {
32    Segments(Vec<Arc<Buffer>>),
33    Frames(Arc<Frames>),
34    Run(Arc<Run>),
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub enum PayloadKind {
39    Segments,
40    Frames,
41    Run,
42}
43
44/// One segment of a run: samples, marked states, and the segment before it.
45#[derive(Clone)]
46pub struct Run {
47    pub samples: Arc<Buffer>,
48    pub marks: BTreeMap<i64, MachineState>,
49    pub parent: Option<Hash>,
50}
51
52impl Run {
53    pub fn end(&self) -> i64 {
54        self.samples.extent().end
55    }
56
57    pub fn bytes(&self) -> usize {
58        let marks: usize = self.marks.values().map(MachineState::bytes).sum();
59        self.samples.len() * self.samples.width() * size_of::<f64>() + marks
60    }
61}
62
63/// The state is the node's own where its samples end, so the samples decide.
64impl PartialEq for Run {
65    fn eq(&self, other: &Run) -> bool {
66        self.samples == other.samples
67    }
68}
69
70impl std::fmt::Debug for Run {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        f.debug_struct("Run")
73            .field("samples", &self.samples.extent())
74            .finish()
75    }
76}
77
78/// An entry not matching this is a miss, never a coercion.
79#[derive(Clone, Copy, Debug, PartialEq, Eq)]
80pub enum Expected {
81    Segments { rate: u32, width: usize },
82    Frames,
83    Run { rate: u32, width: usize },
84}
85
86impl Expected {
87    pub(crate) fn kind(self) -> PayloadKind {
88        match self {
89            Expected::Segments { .. } => PayloadKind::Segments,
90            Expected::Frames => PayloadKind::Frames,
91            Expected::Run { .. } => PayloadKind::Run,
92        }
93    }
94}
95
96#[derive(Clone, Debug, PartialEq)]
97pub struct Entry {
98    pub payload: Payload,
99    /// FORMAT 9.3: the label is part of the value, so a hit answers with the cold run's.
100    pub label: Option<Label>,
101}
102
103impl Payload {
104    pub fn run(self) -> Option<Arc<Run>> {
105        match self {
106            Payload::Run(run) => Some(run),
107            _ => None,
108        }
109    }
110
111    pub fn kind(&self) -> PayloadKind {
112        match self {
113            Payload::Segments(_) => PayloadKind::Segments,
114            Payload::Frames(_) => PayloadKind::Frames,
115            Payload::Run(_) => PayloadKind::Run,
116        }
117    }
118
119    pub fn bytes(&self) -> usize {
120        match self {
121            Payload::Segments(parts) => parts
122                .iter()
123                .map(|b| b.len() * b.width() * size_of::<f64>())
124                .sum(),
125            Payload::Frames(f) => f.width * f.frames * f.bins * 2 * size_of::<f64>(),
126            Payload::Run(run) => run.bytes(),
127        }
128    }
129
130    pub fn answers(&self, expected: Expected) -> bool {
131        match (self, expected) {
132            (Payload::Segments(parts), Expected::Segments { rate, width }) => {
133                parts.iter().all(|b| b.rate == rate && b.width() == width)
134            }
135            (Payload::Frames(_), Expected::Frames) => true,
136            (Payload::Run(run), Expected::Run { rate, width }) => {
137                run.samples.rate == rate && run.samples.width() == width
138            }
139            _ => false,
140        }
141    }
142}
143
144#[derive(Clone, Copy, Debug)]
145pub(crate) struct Question<'p> {
146    pub(crate) grid: Grid,
147    pub(crate) width: usize,
148    pub(crate) profile: &'p Profile,
149    pub(crate) shape: Shape,
150}
151
152#[derive(Clone, Copy, Debug, PartialEq, Eq)]
153pub(crate) enum Shape {
154    Samples,
155    Frames { window: usize, hop: usize },
156}
157
158/// What a value computes and what is asked of it, never where a reader places it.
159pub(crate) fn key(identity: Hash, question: &Question) -> Hash {
160    let Question {
161        grid,
162        width,
163        profile,
164        shape,
165    } = *question;
166    let asked = [
167        grid.a as u64,
168        grid.d as u64,
169        u64::from(grid.rate),
170        width as u64,
171    ];
172    let shape = match shape {
173        Shape::Samples => [0, 0, 0],
174        Shape::Frames { window, hop } => [1, window as u64, hop as u64],
175    };
176    let tag = [0x6b_65_79_00_00_00_00_01];
177    mixed(
178        identity,
179        &[&asked[..], &profile.deciding(), &shape, &tag].concat(),
180    )
181}
182
183pub(crate) fn mixed(seed: Hash, parts: &[u64]) -> Hash {
184    let mut hasher = sva_formula::ContentHasher::new(sva_formula::HashDomain::CacheAddress);
185    hasher.hash(seed);
186    for part in parts {
187        hasher.word(*part);
188    }
189    hasher.finish()
190}
191
192/// `more` laid among `parts`, each touching pair joined into one; a shared part is copied only
193/// to grow.
194pub(crate) fn joined(parts: &mut Vec<Arc<Buffer>>, more: Vec<Arc<Buffer>>) {
195    for part in more {
196        parts.push(part);
197    }
198    parts.sort_by_key(|b| b.start);
199    let mut out: Vec<Arc<Buffer>> = Vec::with_capacity(parts.len());
200    for part in parts.drain(..) {
201        match out.last_mut() {
202            Some(last) if last.extent().end >= part.start => {
203                let from = (last.extent().end - part.start) as usize;
204                if from < part.len() {
205                    let last = Arc::make_mut(last);
206                    for (held, more) in last.planes.iter_mut().zip(&part.planes) {
207                        held.extend_from_slice(&more[from.min(more.len())..]);
208                    }
209                }
210            }
211            _ => out.push(part),
212        }
213    }
214    *parts = out;
215}