tidaldb/tidal/benches/sort.rs
2026-02-23 22:41:16 -07:00

154 lines
4.6 KiB
Rust

#![allow(
clippy::unwrap_used,
clippy::cast_precision_loss,
clippy::cast_possible_truncation
)]
//! Criterion benchmarks for metadata-based sort modes (m6p3).
//!
//! Validates the m6p3 performance acceptance criterion:
//! - Metadata-based sorts (alphabetical, duration) complete in < 50ms at 500 items.
//!
//! Sort modes exercised:
//! - `alphabetical_asc`: packs 8-byte title prefix into u64 for ordering
//! - `alphabetical_desc`: inverse of alphabetical_asc
//! - `shortest`: reads "duration" metadata, orders ascending
//! - `longest`: reads "duration" metadata, orders descending
//!
//! Dataset: 500 items, each with a unique title and duration. No signals.
//! The benchmark uses a shared `LazyLock<TidalDb>` to amortize setup.
use std::sync::LazyLock;
use std::time::Duration;
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use tidaldb::TidalDb;
use tidaldb::query::retrieve::Retrieve;
use tidaldb::schema::{DecaySpec, EntityId, EntityKind, SchemaBuilder, Window};
const N_ITEMS: u64 = 500;
/// Minimal schema: no text fields needed; metadata-only sorts don't use Tantivy.
fn sort_schema() -> tidaldb::schema::Schema {
let mut builder = SchemaBuilder::new();
let _ = builder
.signal(
"view",
EntityKind::Item,
DecaySpec::Exponential {
half_life: Duration::from_secs(7 * 24 * 3600),
},
)
.windows(&[Window::SevenDays])
.velocity(false)
.add();
builder.build().unwrap()
}
/// Build the shared 500-item database. Called exactly once per benchmark run.
///
/// Items are written with:
/// - `"title"`: alphabetically varied titles ("Item 000" through "Item 499")
/// - `"duration"`: varied integer seconds (1..=500)
fn build_sort_db() -> TidalDb {
let db = TidalDb::builder()
.ephemeral()
.with_schema(sort_schema())
.open()
.unwrap();
for i in 0..N_ITEMS {
let item_id = EntityId::new(i + 1);
let mut meta = std::collections::HashMap::new();
// Vary the title so alphabetical sort has real ordering work to do.
meta.insert("title".to_string(), format!("Item {:03}", i));
// Duration cycles 1..=500 seconds.
meta.insert("duration".to_string(), (i + 1).to_string());
db.write_item_with_metadata(item_id, &meta).unwrap();
}
db
}
/// Shared 500-item DB: built once, reused by all sort benchmarks.
static SORT_DB: LazyLock<TidalDb> = LazyLock::new(build_sort_db);
// ── Benchmarks ────────────────────────────────────────────────────────────────
/// Alphabetical A-Z sort over 500 items (< 50ms AC).
fn bench_alphabetical_asc(c: &mut Criterion) {
let db: &TidalDb = &SORT_DB;
let query = Retrieve::builder()
.profile("alphabetical_asc")
.limit(N_ITEMS as usize)
.build()
.unwrap();
let mut group = c.benchmark_group("sort_500");
group.bench_function("alphabetical_asc", |b| {
b.iter(|| db.retrieve(black_box(&query)).unwrap());
});
group.finish();
}
/// Alphabetical Z-A sort over 500 items (< 50ms AC).
fn bench_alphabetical_desc(c: &mut Criterion) {
let db: &TidalDb = &SORT_DB;
let query = Retrieve::builder()
.profile("alphabetical_desc")
.limit(N_ITEMS as usize)
.build()
.unwrap();
let mut group = c.benchmark_group("sort_500");
group.bench_function("alphabetical_desc", |b| {
b.iter(|| db.retrieve(black_box(&query)).unwrap());
});
group.finish();
}
/// Shortest-first sort over 500 items (< 50ms AC).
fn bench_shortest(c: &mut Criterion) {
let db: &TidalDb = &SORT_DB;
let query = Retrieve::builder()
.profile("shortest")
.limit(N_ITEMS as usize)
.build()
.unwrap();
let mut group = c.benchmark_group("sort_500");
group.bench_function("shortest", |b| {
b.iter(|| db.retrieve(black_box(&query)).unwrap());
});
group.finish();
}
/// Longest-first sort over 500 items (< 50ms AC).
fn bench_longest(c: &mut Criterion) {
let db: &TidalDb = &SORT_DB;
let query = Retrieve::builder()
.profile("longest")
.limit(N_ITEMS as usize)
.build()
.unwrap();
let mut group = c.benchmark_group("sort_500");
group.bench_function("longest", |b| {
b.iter(|| db.retrieve(black_box(&query)).unwrap());
});
group.finish();
}
criterion_group!(
benches,
bench_alphabetical_asc,
bench_alphabetical_desc,
bench_shortest,
bench_longest,
);
criterion_main!(benches);