use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use nibli_engine::{EngineLogicalTerm, EngineQueryResult, NibliEngine};
fn fresh_engine() -> NibliEngine {
NibliEngine::new()
}
fn populate_kb(engine: &NibliEngine, n: usize) {
for i in 0..n {
engine
.assert_fact_direct(
"dog".to_string(),
vec![
EngineLogicalTerm::Constant(format!("ent{}", i)),
EngineLogicalTerm::Unspecified,
],
)
.unwrap();
}
}
fn bench_assertion_throughput(c: &mut Criterion) {
let mut group = c.benchmark_group("assertion_throughput");
for &n in &[100, 500, 1000] {
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| {
b.iter_with_setup(NibliEngine::new, |engine| {
populate_kb(&engine, n);
});
});
}
group.finish();
}
fn bench_query_latency(c: &mut Criterion) {
let mut group = c.benchmark_group("query_latency_vs_kb_size");
for &n in &[10, 100, 1000] {
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| {
let engine = fresh_engine();
populate_kb(&engine, n);
engine
.assert_fact_direct(
"dog".to_string(),
vec![
EngineLogicalTerm::Constant("adam".to_string()),
EngineLogicalTerm::Unspecified,
],
)
.unwrap();
assert!(
matches!(
engine.query_holds("dog(Adam).").unwrap(),
EngineQueryResult::True
),
"query_latency bench must measure a hit, not a miss"
);
b.iter(|| {
engine.query_holds("dog(Adam).").unwrap();
});
});
}
group.finish();
}
fn bench_rule_chain(c: &mut Criterion) {
let mut group = c.benchmark_group("rule_chain_depth");
for &depth in &[2usize, 4, 6, 8, 10] {
group.bench_with_input(BenchmarkId::from_parameter(depth), &depth, |b, &depth| {
let engine = fresh_engine();
let preds = [
"dog", "animal", "alive", "big", "fast", "healthy", "thin", "eats", "goes",
"person", "cat",
];
for i in 0..depth.min(preds.len() - 1) {
let text = format!("{}(every {}).", preds[i + 1], preds[i]);
engine.assert_text(&text).unwrap();
}
engine.assert_text("dog(Adam).").unwrap();
let last = preds[depth.min(preds.len() - 1)];
let query = format!("{}(Adam).", last);
assert!(
matches!(engine.query_holds(&query).unwrap(), EngineQueryResult::True),
"rule_chain bench must measure a successful derivation"
);
b.iter(|| {
engine.query_holds(&query).unwrap();
});
});
}
group.finish();
}
fn bench_witness_extraction(c: &mut Criterion) {
let mut group = c.benchmark_group("witness_extraction");
for &n in &[10, 100, 500] {
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| {
let engine = fresh_engine();
populate_kb(&engine, n);
assert!(
!engine.query_find_text("dog(?).").unwrap().is_empty(),
"witness_extraction bench must find witnesses, not time a miss"
);
b.iter(|| {
engine.query_find_text("dog(?).").unwrap();
});
});
}
group.finish();
}
fn bench_equality(c: &mut Criterion) {
let mut group = c.benchmark_group("equality_resolution");
for &n in &[5, 20, 50] {
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| {
let engine = fresh_engine();
engine.assert_text("dog(E0).").unwrap();
for i in 0..n - 1 {
engine
.assert_text(&format!("E{} = E{}.", i, i + 1))
.unwrap();
}
let query = format!("dog(E{}).", n - 1);
assert!(
matches!(engine.query_holds(&query).unwrap(), EngineQueryResult::True),
"equality bench must resolve the chain (dog(E0) reaches E{}), not time a miss",
n - 1
);
b.iter(|| {
engine.query_holds(&query).unwrap();
});
});
}
group.finish();
}
fn bench_retraction_rebuild(c: &mut Criterion) {
let mut group = c.benchmark_group("retraction_rebuild");
for &n in &[10, 100, 500] {
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| {
b.iter_with_setup(
|| {
let engine = fresh_engine();
for i in 0..n {
engine.assert_text(&format!("dog(Ent{}).", i)).unwrap();
}
engine
},
|engine| {
let _ = engine.retract_fact(0);
},
);
});
}
group.finish();
}
fn bench_retraction_incremental(c: &mut Criterion) {
let mut group = c.benchmark_group("retraction_incremental");
for &n in &[10, 100, 500] {
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| {
b.iter_with_setup(
|| {
let engine = fresh_engine();
populate_kb(&engine, n);
engine
},
|engine| {
let _ = engine.retract_fact(0);
},
);
});
}
group.finish();
}
criterion_group!(
benches,
bench_assertion_throughput,
bench_query_latency,
bench_rule_chain,
bench_witness_extraction,
bench_equality,
bench_retraction_rebuild,
bench_retraction_incremental,
);
criterion_main!(benches);