tidaldb/tidal/benches/signals.rs
jx12n 3bcfb3c576 feat: Bazel build, crate docs/ai-lookup, docker images, and engine hardening
- 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
2026-06-07 18:29:38 -06:00

131 lines
4.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#![allow(clippy::unwrap_used)]
use std::time::Duration;
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use tidaldb::{
schema::{DecaySpec, EntityId, EntityKind, SchemaBuilder, Timestamp, Window},
signals::{NoopWalWriter, SignalLedger, SignalTypeId},
};
fn view_ledger() -> (SignalLedger, SignalTypeId) {
let mut builder = SchemaBuilder::new();
let _ = builder
.signal(
"view",
EntityKind::Item,
DecaySpec::Exponential {
half_life: Duration::from_secs(7 * 24 * 3600),
},
)
.windows(&[Window::OneHour, Window::TwentyFourHours, Window::SevenDays])
.velocity(false)
.add();
let schema = builder.build().unwrap();
let type_id = {
// Alphabetical sort assigns "view" → id 0 (only signal).
SignalTypeId::new(0)
};
let ledger = SignalLedger::new(schema, Box::new(NoopWalWriter));
(ledger, type_id)
}
// Pre-computed lambda for a 7-day half-life exponential decay.
const LAMBDA_7D: f64 = std::f64::consts::LN_2 / (7.0 * 24.0 * 3600.0);
/// Benchmark: single signal write (excluding WAL — `NoopWalWriter`).
/// Target: < 100ns.
fn bench_single_signal_write(c: &mut Criterion) {
let (ledger, _type_id) = view_ledger();
let entity_id = EntityId::new(1);
// Fixed timestamp avoids SystemTime::now() overhead per iteration.
let fixed_ns = Timestamp::now().as_nanos();
let ts = Timestamp::from_nanos(fixed_ns);
c.bench_function("signal_write_single", |b| {
b.iter(|| {
ledger
.record_signal(
black_box("view"),
black_box(entity_id),
black_box(1.0_f64),
black_box(ts),
)
.unwrap();
});
});
}
/// Benchmark: decay score read for a single entity.
/// Setup: 100 pre-written signals to ensure a non-trivial hot state.
/// Target: < 100ns.
fn bench_decay_score_read(c: &mut Criterion) {
let (ledger, _type_id) = view_ledger();
let entity_id = EntityId::new(42);
// Pre-warm: 100 signals spread over the past 7 days.
let base_ns = Timestamp::now().as_nanos();
let seven_days_ns: u64 = 7 * 24 * 3600 * 1_000_000_000;
for i in 0u64..100 {
let ts = Timestamp::from_nanos(
base_ns.saturating_sub(seven_days_ns) + i * (seven_days_ns / 100),
);
ledger.record_signal("view", entity_id, 1.0, ts).unwrap();
}
c.bench_function("signal_decay_score_read", |b| {
b.iter(|| {
ledger
.read_decay_score(black_box(entity_id), black_box("view"), black_box(0))
.unwrap()
});
});
}
/// Benchmark: 200-entity scoring pass using direct `DashMap` access to isolate
/// the hot-path read from schema lookup overhead.
/// Setup: 200 entities, each with 50 pre-written signals.
/// Target: < 5µs total.
fn bench_200_entity_scoring_pass(c: &mut Criterion) {
let (ledger, type_id) = view_ledger();
// Pre-warm: 200 entities × 50 signals each.
let base_ns = Timestamp::now().as_nanos();
let entity_ids: Vec<EntityId> = (0u64..200).map(EntityId::new).collect();
for &entity_id in &entity_ids {
for j in 0u64..50 {
let ts = Timestamp::from_nanos(
base_ns.saturating_sub(3_600_000_000_000) + j * 72_000_000_000,
);
ledger.record_signal("view", entity_id, 1.0, ts).unwrap();
}
}
let now_ns = Timestamp::now().as_nanos();
c.bench_function("signal_200_entity_scoring_pass", |b| {
b.iter(|| {
let mut sum = 0.0_f64;
for &entity_id in black_box(&entity_ids) {
if let Some(entry) = ledger.entries().get(&(entity_id, type_id)) {
sum += entry.hot.current_score(
black_box(0),
black_box(now_ns),
black_box(LAMBDA_7D),
);
}
}
black_box(sum)
});
});
}
criterion_group!(
benches,
bench_single_signal_write,
bench_decay_score_read,
bench_200_entity_scoring_pass,
);
criterion_main!(benches);