//! Benchmarks for the chase subsystem. //! //! These are designed to retroactively validate the semi-naive and Skolem //! work and catch future regressions. Each workload runs several chase //! variants over the same input so relative numbers are meaningful. use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; use query_engine::chase::rule::RuleBuilder; use query_engine::chase::{ChaseConfig, ChaseVariant, Rule, chase_with_config}; use query_engine::{Atom, Instance, Term}; fn chain_edges(n: usize) -> Instance { (0..n) .map(|i| { Atom::new( "Edge", vec![ Term::constant(format!("n{}", i)), Term::constant(format!("n{}", i + 1)), ], ) }) .collect() } fn transitive_closure_rules() -> Vec { let edge_to_path = RuleBuilder::new() .when("Edge", vec![Term::var("X"), Term::var("Y")]) .then("Path", vec![Term::var("X"), Term::var("Y")]) .build(); let extend_path = RuleBuilder::new() .when("Path", vec![Term::var("X"), Term::var("Y")]) .when("Edge", vec![Term::var("Y"), Term::var("Z")]) .then("Path", vec![Term::var("X"), Term::var("Z")]) .build(); vec![edge_to_path, extend_path] } fn bench_transitive_closure(c: &mut Criterion) { let mut group = c.benchmark_group("transitive_closure_chain_20"); let instance = chain_edges(20); let rules = transitive_closure_rules(); for (label, variant, semi) in [ ("restricted_naive", ChaseVariant::Restricted, false), ("restricted_semi_naive", ChaseVariant::Restricted, true), ("standard_naive", ChaseVariant::Standard, false), ("standard_semi_naive", ChaseVariant::Standard, true), ] { let config = ChaseConfig { variant, semi_naive: semi, ..Default::default() }; group.bench_function(label, |b| { b.iter_batched( || instance.clone(), |inst| chase_with_config(inst, &rules, config.clone()), BatchSize::SmallInput, ); }); } group.finish(); } fn bench_existentials(c: &mut Criterion) { let mut group = c.benchmark_group("existentials_50_people"); let instance: Instance = (0..50) .map(|i| Atom::new("Person", vec![Term::constant(format!("p{}", i))])) .collect(); let rule = RuleBuilder::new() .when("Person", vec![Term::var("X")]) .then("HasId", vec![Term::var("X"), Term::var("Y")]) .build(); let rules = vec![rule]; for (label, variant) in [ ("restricted", ChaseVariant::Restricted), ("skolem", ChaseVariant::Skolem), ] { let config = ChaseConfig { variant, semi_naive: false, ..Default::default() }; group.bench_function(label, |b| { b.iter_batched( || instance.clone(), |inst| chase_with_config(inst, &rules, config.clone()), BatchSize::SmallInput, ); }); } group.finish(); } criterion_group!(benches, bench_transitive_closure, bench_existentials); criterion_main!(benches);