m12p1 (measurement truth): TidalDb::vector_search_items pure k-NN probe + POST /vector_search (standalone + region node, merge-by-distance) + tidal-stress --verify-recall (deterministic id-keyed corpus, in-RAM brute-force cosine oracle, open-loop ramp → recall@k + true p99 + read-knee + JSON/gate exit). Repaired fabricated p99 columns (mean-as-p99) in social-scale.md / scale.rs. Verified real: recall@10=0.9997 at 20k/1536-D vs brute-force. m12p2 (G1 unblock): ANN candidate-gen wired into RETRIEVE — for_you=preference vector, related=seed embedding (similar_to), graceful scan-fallback. Cached per-signal-type top-K (signals/ledger/hot_top_k.rs, decay-order-invariant) so trending serves O(K). related over HTTP (FeedQuery.similar_to). Harness gains --feed-profile / --seed-preferences. Verified: trending retrieve p99 3.5-7.7ms. m12p3 (G2): per-query ef_search now honored (RwLock epoch-guard with_expansion, shared guard for same-ef concurrency) + dimension-aware brute→HNSW crossover usearch_min_vectors(dim) + memory_usage() + examples/ann_grid_search.rs. Measured 1536-D/100k clustered: default M=16/ef_c=400/F16/ef_s=200 clears G1+G2 (recall 0.997, p99 1.4ms); F16 -0.25% vs F32; Int8 rejected (-28%). Recall corpus is now clustered (Gaussian mixture) in grid + harness.
83 lines
3.3 KiB
Rust
83 lines
3.3 KiB
Rust
//! Hot-path micro-benchmarks for the load generator's OWN per-request cost.
|
|
//!
|
|
//! These run with NO server and NO network, so they isolate exactly the work
|
|
//! the generator does per dispatched request — request construction
|
|
//! ([`Workload::next`]) and latency aggregation ([`LatencyHistogram::record`]).
|
|
//! That is the only honest way to answer "is this allocation material?": the
|
|
//! number here is the generator's CPU floor, the thing that must stay far below
|
|
//! the network round-trip it measures, and the before/after the perf sweep is
|
|
//! graded against.
|
|
|
|
use std::hint::black_box;
|
|
use std::time::Duration;
|
|
|
|
use criterion::{Criterion, criterion_group, criterion_main};
|
|
|
|
use tidal_stress::metrics::LatencyHistogram;
|
|
use tidal_stress::workload::{Workload, parse_mix};
|
|
|
|
fn make_workload(mix: &str) -> Workload {
|
|
Workload::new(
|
|
// Three read gateways (round-robin), writes pinned to one gateway =>
|
|
// write_bases len == 1, the headline `--leader-url` path.
|
|
vec![
|
|
"http://10.0.0.1:9500".into(),
|
|
"http://10.0.0.2:9500".into(),
|
|
"http://10.0.0.3:9500".into(),
|
|
],
|
|
vec!["http://10.0.0.1:9500".into()],
|
|
parse_mix(mix).expect("mix preset parses"),
|
|
10_000, // corpus
|
|
50_000, // users
|
|
1.3, // hot_skew
|
|
24, // feed_limit
|
|
128, // embedding_dim
|
|
None, // forced_profile (use the weighted feed-profile mix)
|
|
)
|
|
}
|
|
|
|
/// `Workload::next` — the per-dispatched-request construction cost (URL String +
|
|
/// body). `peach` is the headline signal-dominated mix; `writes` forces the body
|
|
/// path on every call; `reads` isolates the URL-only feed/search path.
|
|
fn bench_next(c: &mut Criterion) {
|
|
let wl_peach = make_workload("peach");
|
|
let wl_writes = make_workload("writes");
|
|
let wl_reads = make_workload("reads");
|
|
let mut rng = rand::rng();
|
|
|
|
let mut g = c.benchmark_group("workload_next");
|
|
g.bench_function("peach", |b| b.iter(|| black_box(wl_peach.next(&mut rng))));
|
|
g.bench_function("writes", |b| b.iter(|| black_box(wl_writes.next(&mut rng))));
|
|
g.bench_function("reads", |b| b.iter(|| black_box(wl_reads.next(&mut rng))));
|
|
g.finish();
|
|
}
|
|
|
|
/// `LatencyHistogram::record` — the per-OK-request collector cost (single-writer,
|
|
/// allocation-free), plus the per-stage `percentile` report cost.
|
|
fn bench_histogram(c: &mut Criterion) {
|
|
c.bench_function("histogram_record", |b| {
|
|
let mut h = LatencyHistogram::default();
|
|
// Cheap LCG so successive records land in different buckets rather than
|
|
// hammering one — closer to a real latency distribution.
|
|
let mut state = 0x2545_F491_4F6C_DD1Du64;
|
|
b.iter(|| {
|
|
state = state
|
|
.wrapping_mul(6_364_136_223_846_793_005)
|
|
.wrapping_add(1_442_695_040_888_963_407);
|
|
let ns = (state >> 33) % 200_000_000 + 1; // ~1ns..200ms
|
|
h.record(black_box(Duration::from_nanos(ns)));
|
|
});
|
|
});
|
|
|
|
let mut h = LatencyHistogram::default();
|
|
for i in 1..=100_000u64 {
|
|
h.record(Duration::from_nanos(i * 1_500));
|
|
}
|
|
c.bench_function("histogram_p99", |b| {
|
|
b.iter(|| black_box(h.percentile(black_box(0.99))));
|
|
});
|
|
}
|
|
|
|
criterion_group!(benches, bench_next, bench_histogram);
|
|
criterion_main!(benches);
|