2026-06-04 15:43:38 +02:00

119 lines
4.1 KiB
Rust

//! 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<RowId> {
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);