//! Geomerge-specific benchmarks. //! //! Geomerge has a different shape from the KV adapters: theory-defined schema, //! foreign-key value type, law validation at commit, and pending row id //! resolution via `CommittedTx`. The three workloads here answer questions //! specific to that shape: //! //! - `theory_load`: deserialize the bundled `paths.json` and build the //! `GeomergeStorage`. One-shot, not parameterized. //! - `insert_commit_n`: in one transaction, insert prerequisite Graphs / G0 //! / G1 rows and then N vertices in a host graph, then commit. Commit cost //! reflects geomerge's law validation over the new state. //! - `resolve_pending_n`: after a commit that produced N pending RowIds, //! resolve each via `CommittedTx::resolve`. Measures the cost of mapping //! in-flight ids to their post-commit form. //! //! Requires the `geomerge` feature; the bench is gated via //! `required-features` in `Cargo.toml`. #![allow(clippy::unwrap_used, clippy::expect_used)] use criterion::{BatchSize, BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; use geomerge::ir::FlatTheory; use storage::adapters::geomerge::GeomergeStorage; use storage::id::RowId; use storage::value::Value; use storage::{Storage, Transaction}; const PATHS_SCHEMA_JSON: &str = include_str!("../../../external/geomerge/crates/geomerge/tests/data/paths.json"); const ROW_COUNTS: &[usize] = &[10, 100, 1_000]; fn load_theory() -> FlatTheory { serde_json::from_str(PATHS_SCHEMA_JSON).unwrap() } fn fresh_storage() -> GeomergeStorage { GeomergeStorage::from_theory(load_theory()).unwrap() } /// Insert the schema-required Graphs, G0, and G1 rows. Returns the RowId of /// the "host" graph that subsequent vertex inserts should reference. fn build_prereqs(tx: &mut dyn Transaction) -> RowId { let designated = tx.insert("Graphs", vec![]).unwrap(); let host = tx.insert("Graphs", vec![]).unwrap(); tx.insert("G0", vec![Value::Id(designated.clone())]) .unwrap(); tx.insert("G1", vec![Value::Id(designated)]).unwrap(); host } fn insert_n_vertices(tx: &mut dyn Transaction, host: &RowId, n: usize) -> Vec { let mut pending = Vec::with_capacity(n); for _ in 0..n { pending.push(tx.insert("G.V", vec![Value::Id(host.clone())]).unwrap()); } pending } fn bench_theory_load(c: &mut Criterion) { c.bench_function("theory_load", |b| { b.iter(|| { let theory = load_theory(); let storage = GeomergeStorage::from_theory(theory).unwrap(); black_box(storage); }); }); } fn bench_insert_commit_n(c: &mut Criterion) { let mut group = c.benchmark_group("insert_commit_n"); for &n in ROW_COUNTS { group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| { b.iter_batched( fresh_storage, |mut storage| { let mut tx = storage.transaction().unwrap(); let host = build_prereqs(&mut *tx); let _ = insert_n_vertices(&mut *tx, &host, n); tx.commit().unwrap(); black_box(storage); }, BatchSize::SmallInput, ); }); } group.finish(); } fn bench_resolve_pending_n(c: &mut Criterion) { let mut group = c.benchmark_group("resolve_pending_n"); for &n in ROW_COUNTS { group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| { // One-time setup outside the timed window: build a CommittedTx // with N pending vertex IDs. let mut storage = fresh_storage(); let mut tx = storage.transaction().unwrap(); let host = build_prereqs(&mut *tx); let pending = insert_n_vertices(&mut *tx, &host, n); let committed = tx.commit().unwrap(); b.iter(|| { for p in &pending { black_box(committed.resolve(p)); } }); }); } group.finish(); } criterion_group!( benches, bench_theory_load, bench_insert_commit_n, bench_resolve_pending_n ); criterion_main!(benches);