//! Search precision benchmarks for implicit AND operator change. //! //! This benchmark suite measures the performance impact of changing the implicit //! query operator from OR to AND. It verifies that the precision improvement //! (33% → 100%) comes with no query latency regression. //! //! # Benchmarks //! //! - `query_two_words`: Measures latency for simple two-word queries //! - `precision_calculation`: Measures precision metrics and filtering overhead //! - `result_count`: Measures result set size impact //! //! # Running //! //! ```bash //! cargo bench --bench search_precision_benchmark --features lex //! ``` use criterion::{Criterion, criterion_group, criterion_main}; use memvid_core::{Memvid, PutOptions, SearchRequest}; use std::time::Instant; /// Setup test corpus fn setup_corpus(size: usize) -> std::path::PathBuf { let temp_file = std::env::temp_dir().join(format!("bench_{}.mv2", size)); let _ = std::fs::remove_file(&temp_file); let mut mem = Memvid::create(&temp_file).unwrap(); let topics = [ "machine learning neural networks", "python programming development", "machine learning with python", "rust systems programming", "web development javascript", ]; for i in 0..size { let content = format!("Document {} about {}", i, topics[i % topics.len()]); mem.put_bytes_with_options( content.as_bytes(), PutOptions::builder().title(format!("Doc {}", i)).build(), ) .unwrap(); if (i + 1) % 100 == 0 { mem.commit().unwrap(); } } mem.commit().unwrap(); temp_file } fn bench_query_latency(c: &mut Criterion) { let corpus_path = setup_corpus(1000); c.bench_function("query_two_words", |b| { b.iter_custom(|iters| { let mut total = std::time::Duration::ZERO; for _ in 0..iters { let mut mem = Memvid::open(&corpus_path).unwrap(); // FIX: mut let start = Instant::now(); let _results = mem .search(SearchRequest { query: "machine learning".to_string(), top_k: 10, snippet_chars: 200, uri: None, scope: None, cursor: None, #[cfg(feature = "temporal_track")] temporal: None, as_of_frame: None, as_of_ts: None, no_sketch: false, acl_context: None, acl_enforcement_mode: memvid_core::types::AclEnforcementMode::Audit, }) .unwrap(); total += start.elapsed(); } total }); }); std::fs::remove_file(&corpus_path).ok(); } fn bench_precision(c: &mut Criterion) { let corpus_path = setup_corpus(1000); c.bench_function("precision_calculation", |b| { b.iter_custom(|iters| { let mut total = std::time::Duration::ZERO; for _ in 0..iters { let mut mem = Memvid::open(&corpus_path).unwrap(); // FIX: mut let start = Instant::now(); let results = mem .search(SearchRequest { query: "machine python".to_string(), top_k: 100, snippet_chars: 200, uri: None, scope: None, cursor: None, #[cfg(feature = "temporal_track")] temporal: None, as_of_frame: None, as_of_ts: None, no_sketch: false, acl_context: None, acl_enforcement_mode: memvid_core::types::AclEnforcementMode::Audit, }) .unwrap(); let _relevant = results .hits .iter() .filter(|hit| { let text = hit.text.to_lowercase(); text.contains("machine") && text.contains("python") }) .count(); total += start.elapsed(); } total }); }); std::fs::remove_file(&corpus_path).ok(); } fn bench_result_count(c: &mut Criterion) { let corpus_path = setup_corpus(1000); c.bench_function("result_count", |b| { b.iter_custom(|iters| { let mut total = std::time::Duration::ZERO; for _ in 0..iters { let mut mem = Memvid::open(&corpus_path).unwrap(); // FIX: mut let start = Instant::now(); let results = mem .search(SearchRequest { query: "machine learning".to_string(), top_k: 100, snippet_chars: 200, uri: None, scope: None, cursor: None, #[cfg(feature = "temporal_track")] temporal: None, as_of_frame: None, as_of_ts: None, no_sketch: false, acl_context: None, acl_enforcement_mode: memvid_core::types::AclEnforcementMode::Audit, }) .unwrap(); let _count = results.hits.len(); total += start.elapsed(); } total }); }); std::fs::remove_file(&corpus_path).ok(); } criterion_group!( benches, bench_query_latency, bench_precision, bench_result_count ); criterion_main!(benches);