80 lines
3.7 KiB
Rust
80 lines
3.7 KiB
Rust
//! Small prepared SQLite instrumentation diagnostic; not a production benchmark.
|
|||
|
|
use anyhow::{ensure, Result};
|
||
|
|
use searchi_core::SearchIndex;
|
||
|
|
use std::{fs, path::Path, time::Instant};
|
||
|
|
|
||
|
|
fn distribution(samples: &[f64]) -> serde_json::Value {
|
||
|
|
let mut sorted = samples.to_vec();
|
||
|
|
sorted.sort_by(f64::total_cmp);
|
||
|
|
let percentile = |fraction: f64| {
|
||
|
|
sorted[((sorted.len() as f64 * fraction).ceil() as usize).saturating_sub(1)]
|
||
|
|
};
|
||
|
|
serde_json::json!({"samples":sorted.len(),"p50_ms":percentile(0.50),"p95_ms":percentile(0.95),"mean_ms":sorted.iter().sum::<f64>()/sorted.len() as f64})
|
||
|
|
}
|
||
|
|
fn main() -> Result<()> {
|
||
|
|
let fixture = tempfile::tempdir()?;
|
||
|
|
let root = fixture.path().join("files");
|
||
|
|
fs::create_dir(&root)?;
|
||
|
|
for index in 0..1000 {
|
||
|
|
fs::write(
|
||
|
|
root.join(format!("record-{index:04}.txt")),
|
||
|
|
format!(
|
||
|
|
"Supplier agreement {index}. Cancellation needs written notice. Group {}.",
|
||
|
|
index % 31
|
||
|
|
),
|
||
|
|
)?;
|
||
|
|
}
|
||
|
|
let mut index = SearchIndex::open(fixture.path().join("index.sqlite"))?;
|
||
|
|
let report = index.index_folder(&root)?;
|
||
|
|
index.prewarm()?;
|
||
|
|
ensure!(report.indexed == 1000, "Unexpected fixture indexing count");
|
||
|
|
let queries: Vec<String> = (0..24)
|
||
|
|
.map(|index| format!("record-{:04}.txt", index * 37))
|
||
|
|
.collect();
|
||
|
|
for query in &queries {
|
||
|
|
index.search_measured(query, 10, false)?;
|
||
|
|
index.search_measured(query, 10, true)?;
|
||
|
|
}
|
||
|
|
let mut basic = Vec::new();
|
||
|
|
let mut debug = Vec::new();
|
||
|
|
let mut trace_spans = Vec::new();
|
||
|
|
// Alternate order to reduce monotonic cache/CPU-order effects.
|
||
|
|
for repetition in 0..20 {
|
||
|
|
for query in &queries {
|
||
|
|
for detailed in if repetition % 2 == 0 {
|
||
|
|
[false, true]
|
||
|
|
} else {
|
||
|
|
[true, false]
|
||
|
|
} {
|
||
|
|
let started = Instant::now();
|
||
|
|
let response = index.search_measured(query, 10, detailed)?;
|
||
|
|
let elapsed = started.elapsed().as_secs_f64() * 1000.0;
|
||
|
|
ensure!(
|
||
|
|
response
|
||
|
|
.results
|
||
|
|
.first()
|
||
|
|
.is_some_and(|result| &result.filename == query),
|
||
|
|
"Fixture retrieval changed"
|
||
|
|
);
|
||
|
|
if detailed {
|
||
|
|
trace_spans.push(response.metrics.trace.as_ref().unwrap().len());
|
||
|
|
debug.push(elapsed);
|
||
|
|
} else {
|
||
|
|
ensure!(
|
||
|
|
response.metrics.trace.is_none(),
|
||
|
|
"Basic unexpectedly stored traces"
|
||
|
|
);
|
||
|
|
basic.push(elapsed);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
let output = serde_json::json!({"kind":"prepared_sqlite_trace_overhead","production_claim":false,"profile":if cfg!(debug_assertions){"debug"}else{"release"},"os":std::env::consts::OS,"arch":std::env::consts::ARCH,"files":1000,"queries":24,"repetitions":20,"measurement":"outer Rust call including metrics assembly; excludes fixture setup/JSON serialization","basic":distribution(&basic),"debug":distribution(&debug),"basic_samples_ms":basic,"debug_samples_ms":debug,"debug_span_count_min":trace_spans.iter().min(),"debug_span_count_max":trace_spans.iter().max(),"limits":"tiny warm lexical corpus; harness serial/no model/media/GPU; other VM work including native photo inference may contend; not isolated production performance"});
|
||
|
|
let serialized = serde_json::to_string_pretty(&output)?;
|
||
|
|
if let Some(path) = std::env::args_os().nth(1) {
|
||
|
|
fs::write(Path::new(&path), serialized)?;
|
||
|
|
} else {
|
||
|
|
println!("{serialized}");
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|