benches/store.rs (criterion, in `just bench`): open 249 ms, viewer load 616 ms, full verify 809 ms, post one entry (four synced appends) 62 ms. The books are built on a temp dir and copied under target/ so every sync hits a real disk. Baselines in docs/testing.md; no budget, by decision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
1 parent
fb3b4ab476
commit
2cbb6a263f
5 files changed
+116
-1
No files matched your search
@@ -21,6 +21,11 @@ hex = "0.4"
|
||||
tempfile = "3"
|
||||
proptest = "1"
|
||||
rust_decimal = "1"
|
||||
criterion = "0.8.2"
|
||||
|
||||
[[bench]]
|
||||
name = "store"
|
||||
harness = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -0,0 +1,98 @@
|
||||
//! Benchmarks for the store's hot paths (rank 6: a baseline, never
|
||||
//! correctness; no budget, by decision, 2026-10-13).
|
||||
//!
|
||||
//! `cargo bench -p ledger-store --bench store`. Books of 10k two-line
|
||||
//! entries, on a real disk (under `target/`), since every journal append
|
||||
//! is synced and a RAM disk would hide that: opening them (verify the hash
|
||||
//! chain, replay, catch the database up), what the viewer does to load
|
||||
//! them, a full verify, and posting one more entry (four synced appends).
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use criterion::{Criterion, criterion_group, criterion_main};
|
||||
use ledger_core::{AccountType, Currency, Money, Side};
|
||||
use ledger_store::{Snapshot, Store};
|
||||
use rust_decimal::Decimal;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const ENTRIES: u32 = 10_000;
|
||||
|
||||
fn post(store: &mut Store, n: u32, a: u64, b: u64) {
|
||||
let day = NaiveDate::from_ymd_opt(2026, 1, 1).unwrap() + chrono::Days::new(u64::from(n % 365));
|
||||
let e = store.create_entry(day, "bench").unwrap();
|
||||
let m = Money::new(Decimal::new(i64::from(n % 50_000) + 1, 2), Currency::USD);
|
||||
store.add_line(e, a, Side::Debit, m).unwrap();
|
||||
store.add_line(e, b, Side::Credit, m).unwrap();
|
||||
store.post_entry(e).unwrap();
|
||||
}
|
||||
|
||||
fn copy_dir(from: &Path, to: &Path) {
|
||||
std::fs::create_dir_all(to).unwrap();
|
||||
for e in std::fs::read_dir(from).unwrap() {
|
||||
let e = e.unwrap();
|
||||
let target = to.join(e.file_name());
|
||||
if e.file_type().unwrap().is_dir() {
|
||||
copy_dir(&e.path(), &target);
|
||||
} else {
|
||||
std::fs::copy(e.path(), target).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 10k entries over 40 accounts, built in a temp dir (fast) and copied to
|
||||
/// disk under target/ (honest syncs). Built once per run.
|
||||
fn books() -> PathBuf {
|
||||
let disk = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../target/bench-books");
|
||||
let _ = std::fs::remove_dir_all(&disk);
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
{
|
||||
let mut store = Store::create(tmp.path(), Currency::USD).unwrap();
|
||||
let types = [
|
||||
AccountType::Asset,
|
||||
AccountType::Liability,
|
||||
AccountType::Equity,
|
||||
AccountType::Revenue,
|
||||
AccountType::Expense,
|
||||
];
|
||||
let ids: Vec<u64> = (0..40)
|
||||
.map(|i| {
|
||||
store
|
||||
.add_account(&format!("{}", 1000 + i), "a", types[i % 5], None, false)
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
for n in 0..ENTRIES {
|
||||
let a = ids[(n as usize * 7) % 40];
|
||||
let b = ids[(n as usize * 13 + 1) % 40];
|
||||
if a != b {
|
||||
post(&mut store, n, a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = std::fs::remove_file(tmp.path().join(ledger_store::LOCK_FILE));
|
||||
copy_dir(tmp.path(), &disk);
|
||||
disk
|
||||
}
|
||||
|
||||
fn bench(c: &mut Criterion) {
|
||||
let dir = books();
|
||||
let mut g = c.benchmark_group("store_10k");
|
||||
g.sample_size(10);
|
||||
g.bench_function("open", |b| b.iter(|| Store::open(&dir).unwrap()));
|
||||
g.bench_function("viewer_load", |b| b.iter(|| Snapshot::load(&dir).unwrap()));
|
||||
g.bench_function("verify", |b| {
|
||||
let store = Store::open(&dir).unwrap();
|
||||
b.iter(|| store.verify().unwrap())
|
||||
});
|
||||
g.bench_function("post_one_entry", |b| {
|
||||
let mut store = Store::open(&dir).unwrap();
|
||||
let mut n = ENTRIES;
|
||||
b.iter(|| {
|
||||
n += 1;
|
||||
post(&mut store, n, 1, 2);
|
||||
})
|
||||
});
|
||||
g.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench);
|
||||
criterion_main!(benches);
|
||||
Reference in new issue
Block a user