Skip to main content

forge_workspace/store/
bench_results.rs

1//! What this machine's benches measured, keyed by what they were about.
2//!
3//! App-level rather than per-project: a bench runs one model on this
4//! machine's own material, and the corpus is the machine's too. **The key
5//! is `(role, file, tier, corpus sha)`** - a result for a different corpus
6//! is not a comparison, and overwriting the row for the same corpus is
7//! what keeps a re-run from stacking a second copy of the same number.
8
9use redb::{ReadableTable, TableDefinition};
10
11use crate::bench::BenchResult;
12
13use super::Db;
14
15/// One row per (role, file, tier, corpus), keyed by the four of them.
16const RESULTS: TableDefinition<&str, &[u8]> = TableDefinition::new("bench_results");
17
18/// The key one result is filed under.
19pub fn key(result: &BenchResult) -> String {
20    key_parts(result.target.role, &result.target.file, result.tier, &result.corpus.sha256)
21}
22
23/// The key's own spelling, for a caller that holds the parts rather than
24/// the row - the page's delete names them and nothing else.
25pub fn key_parts(
26    role: crate::bench::BenchRole,
27    file: &str,
28    tier: forge_dictate::bench::Tier,
29    corpus: &str,
30) -> String {
31    format!("{role:?}|{file}|{tier:?}|{corpus}")
32}
33
34/// Every saved result, newest first.
35pub fn results(db: &Db) -> anyhow::Result<Vec<BenchResult>> {
36    let txn = db.database().begin_read()?;
37    let table = match txn.open_table(RESULTS) {
38        Ok(table) => table,
39        Err(redb::TableError::TableDoesNotExist(_)) => return Ok(Vec::new()),
40        Err(error) => return Err(error.into()),
41    };
42    let mut rows = Vec::new();
43    for entry in table.iter()? {
44        let (_, value) = entry?;
45        rows.push(serde_json::from_slice(value.value())?);
46    }
47    rows.sort_by(|a: &BenchResult, b: &BenchResult| b.at.cmp(&a.at));
48    Ok(rows)
49}
50
51/// Drop one result by its key. A key with no row is not an error: the row
52/// the page asked to delete is already gone.
53pub fn remove(db: &Db, key: &str) -> anyhow::Result<()> {
54    let txn = db.database().begin_write()?;
55    {
56        let mut table = match txn.open_table(RESULTS) {
57            Ok(table) => table,
58            Err(redb::TableError::TableDoesNotExist(_)) => return Ok(()),
59            Err(error) => return Err(error.into()),
60        };
61        table.remove(key)?;
62    }
63    txn.commit()?;
64    Ok(())
65}
66
67/// Save one result, replacing any row for the same (role, file, tier,
68/// corpus).
69pub fn save(db: &Db, result: &BenchResult) -> anyhow::Result<()> {
70    let txn = db.database().begin_write()?;
71    {
72        let mut table = txn.open_table(RESULTS)?;
73        table.insert(key(result).as_str(), serde_json::to_vec(result)?.as_slice())?;
74    }
75    txn.commit()?;
76    Ok(())
77}