- Add BUILD.bazel across tidal, tidal-net, tidal-server, tidalctl for bzlmod build - Add tidal/ crate docs (README, CHANGELOG, CONTRIBUTING, AGENTS, CLAUDE, API, ARCHITECTURE) and ai-lookup reference - Add docker standalone/cluster/deploy images, compose, and prometheus config - Harden WAL (batch format, writer, dedup, diagnostics), text syncer/collectors, and vector registry - Expand tidalctl CLI and tests; restructure WAL/visibility integration test suites - Refine tidal-net transport/client/server and tidal-server cluster/scatter-gather
138 lines
4.1 KiB
Rust
138 lines
4.1 KiB
Rust
#![allow(clippy::unwrap_used)]
|
|
//! Criterion benchmarks for the BM25 text index pipeline.
|
|
//!
|
|
//! Measures BM25 query latency at various corpus sizes to validate the
|
|
//! < 10ms target at 10K documents specified in the m5p1 phase acceptance criteria.
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
|
use tidaldb::{
|
|
schema::{EntityId, TextFieldDef, TextFieldType},
|
|
text::{AllScoresCollector, TextIndex},
|
|
};
|
|
|
|
fn make_index(n: u64) -> TextIndex {
|
|
let fields = vec![
|
|
TextFieldDef {
|
|
key: "title".into(),
|
|
field_type: TextFieldType::Text,
|
|
},
|
|
TextFieldDef {
|
|
key: "description".into(),
|
|
field_type: TextFieldType::Text,
|
|
},
|
|
TextFieldDef {
|
|
key: "category".into(),
|
|
field_type: TextFieldType::Keyword,
|
|
},
|
|
];
|
|
|
|
let idx = TextIndex::ephemeral(&fields).unwrap();
|
|
let mut w = idx.writer_guard().unwrap();
|
|
|
|
for i in 0..n {
|
|
let mut meta = HashMap::new();
|
|
// Vary titles so BM25 IDF scoring is meaningful.
|
|
meta.insert(
|
|
"title".into(),
|
|
format!("Rust tutorial {i} async concurrency"),
|
|
);
|
|
meta.insert(
|
|
"description".into(),
|
|
"Learn Rust programming with practical examples and real projects.".into(),
|
|
);
|
|
// Alternate categories to test keyword field throughput.
|
|
let cat = if i % 2 == 0 { "programming" } else { "systems" };
|
|
meta.insert("category".into(), cat.into());
|
|
w.index_item(EntityId::new(i), &meta).unwrap();
|
|
}
|
|
|
|
w.commit(n).unwrap();
|
|
drop(w);
|
|
|
|
idx.reload_reader().unwrap();
|
|
idx
|
|
}
|
|
|
|
/// BM25 bare-term query at 1K docs.
|
|
fn bench_bm25_1k(c: &mut Criterion) {
|
|
let idx = make_index(1_000);
|
|
let searcher = idx.searcher();
|
|
let parser = idx.query_parser();
|
|
let collector = AllScoresCollector {
|
|
entity_id_field: idx.fields().entity_id,
|
|
};
|
|
|
|
c.bench_function("bm25_query_1k_docs", |b| {
|
|
b.iter(|| {
|
|
let q = parser.parse(black_box("Rust async")).unwrap();
|
|
let results = searcher.search(q.as_ref(), &collector).unwrap();
|
|
black_box(results)
|
|
});
|
|
});
|
|
}
|
|
|
|
/// BM25 bare-term query at 10K docs — must complete in < 10ms.
|
|
fn bench_bm25_10k(c: &mut Criterion) {
|
|
let idx = make_index(10_000);
|
|
let searcher = idx.searcher();
|
|
let parser = idx.query_parser();
|
|
let collector = AllScoresCollector {
|
|
entity_id_field: idx.fields().entity_id,
|
|
};
|
|
|
|
c.bench_function("bm25_query_10k_docs", |b| {
|
|
b.iter(|| {
|
|
let q = parser.parse(black_box("Rust async")).unwrap();
|
|
let results = searcher.search(q.as_ref(), &collector).unwrap();
|
|
black_box(results)
|
|
});
|
|
});
|
|
}
|
|
|
|
/// BM25 exact-phrase query at 10K docs.
|
|
fn bench_bm25_phrase_10k(c: &mut Criterion) {
|
|
let idx = make_index(10_000);
|
|
let searcher = idx.searcher();
|
|
let parser = idx.query_parser();
|
|
let collector = AllScoresCollector {
|
|
entity_id_field: idx.fields().entity_id,
|
|
};
|
|
|
|
c.bench_function("bm25_phrase_10k_docs", |b| {
|
|
b.iter(|| {
|
|
let q = parser.parse(black_box("\"Rust programming\"")).unwrap();
|
|
let results = searcher.search(q.as_ref(), &collector).unwrap();
|
|
black_box(results)
|
|
});
|
|
});
|
|
}
|
|
|
|
/// BM25 keyword field-scoped query at 10K docs.
|
|
fn bench_bm25_keyword_10k(c: &mut Criterion) {
|
|
let idx = make_index(10_000);
|
|
let searcher = idx.searcher();
|
|
let parser = idx.query_parser();
|
|
let collector = AllScoresCollector {
|
|
entity_id_field: idx.fields().entity_id,
|
|
};
|
|
|
|
c.bench_function("bm25_keyword_10k_docs", |b| {
|
|
b.iter(|| {
|
|
let q = parser.parse(black_box("category:programming")).unwrap();
|
|
let results = searcher.search(q.as_ref(), &collector).unwrap();
|
|
black_box(results)
|
|
});
|
|
});
|
|
}
|
|
|
|
criterion_group!(
|
|
bm25_benches,
|
|
bench_bm25_1k,
|
|
bench_bm25_10k,
|
|
bench_bm25_phrase_10k,
|
|
bench_bm25_keyword_10k
|
|
);
|
|
criterion_main!(bm25_benches);
|