diff --git a/Cargo.toml b/Cargo.toml index c5c378f8..a499cda6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -176,17 +176,24 @@ harness = false name = "cache_ingest" harness = false +[[bench]] +name = "cache_metadata_payload" +harness = false + [[bench]] name = "end_to_end_proxy" harness = false [profile.dev] +debug = 0 # Use the profiling/bench profiles when symbols are needed +incremental = false # Avoid retaining a second full set of object files split-debuginfo = "unpacked" # Faster linking — don't bundle debuginfo into binary [profile.dev.package."*"] opt-level = 1 # Compile dependencies with optimizations in dev mode # Huge runtime speedup for rustls/ring/foyer/moka # Minimal compile-time cost (deps cached after first build) +debug = 0 # Keep dependency artifacts compact in the dev profile [profile.release] debug = false # No debug symbols for smaller binaries diff --git a/benches/cache_metadata_payload.rs b/benches/cache_metadata_payload.rs new file mode 100644 index 00000000..22826ac5 --- /dev/null +++ b/benches/cache_metadata_payload.rs @@ -0,0 +1,120 @@ +//! Mixed hybrid-cache workload for metadata and retained-payload updates. +//! +//! Run with: `cargo bench --bench cache_metadata_payload` + +use divan::Bencher; +use nntp_proxy::cache::{HybridCacheConfig, UnifiedCache}; +use nntp_proxy::protocol::StatusCode; +use nntp_proxy::types::{BackendId, MessageId}; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::Duration; +use tempfile::{TempDir, tempdir}; + +const ARTICLE_BODY: &str = "x"; + +fn main() { + divan::main(); +} + +fn benchmark_cache() -> (tokio::runtime::Runtime, TempDir, UnifiedCache) { + let runtime = tokio::runtime::Runtime::new().expect("benchmark runtime"); + let directory = tempdir().expect("benchmark cache directory"); + let config = HybridCacheConfig { + memory_capacity: 4 * 1024 * 1024, + disk_capacity: 64 * 1024 * 1024, + disk_path: directory.path().to_path_buf(), + ttl: Duration::from_secs(300), + compression: nntp_proxy::config::CompressionCodec::None, + shards: 16, + }; + let cache = runtime + .block_on(UnifiedCache::hybrid(config)) + .expect("hybrid cache"); + (runtime, directory, cache) +} + +fn message_id(sequence: u64) -> MessageId<'static> { + MessageId::new(format!("")).expect("benchmark message ID") +} + +fn article_response(sequence: u64) -> Vec { + format!( + "220 42 \r\nSubject: Benchmark\r\n\r\n{ARTICLE_BODY}\r\n.\r\n" + ) + .into_bytes() +} + +#[divan::bench(sample_count = 20, sample_size = 1)] +fn metadata_only_updates(bencher: Bencher) { + let (runtime, _directory, cache) = benchmark_cache(); + let sequence = AtomicU64::new(0); + + bencher + .with_inputs(|| message_id(sequence.fetch_add(1, Ordering::Relaxed))) + .bench_values(|id| { + runtime.block_on(cache.record_backend_has_status( + id, + StatusCode::new(223), + BackendId::from_index(0), + 0.into(), + )); + }); + + runtime + .block_on(cache.close()) + .expect("close benchmark cache"); +} + +#[divan::bench(sample_count = 20, sample_size = 1)] +fn retained_payload_updates(bencher: Bencher) { + let (runtime, _directory, cache) = benchmark_cache(); + let sequence = AtomicU64::new(0); + + bencher + .with_inputs(|| { + let sequence = sequence.fetch_add(1, Ordering::Relaxed); + (message_id(sequence), article_response(sequence)) + }) + .bench_values(|(id, response)| { + runtime.block_on(cache.upsert_ingest(id, response, BackendId::from_index(0), 0.into())); + }); + + runtime + .block_on(cache.close()) + .expect("close benchmark cache"); +} + +#[divan::bench(sample_count = 20, sample_size = 1)] +fn mixed_metadata_and_payload_updates(bencher: Bencher) { + let (runtime, _directory, cache) = benchmark_cache(); + let sequence = AtomicU64::new(0); + + bencher + .with_inputs(|| { + let sequence = sequence.fetch_add(2, Ordering::Relaxed); + ( + message_id(sequence), + message_id(sequence + 1), + article_response(sequence + 1), + ) + }) + .bench_values(|(metadata_id, payload_id, response)| { + runtime.block_on(async { + cache + .record_backend_has_status( + metadata_id, + StatusCode::new(223), + BackendId::from_index(0), + 0.into(), + ) + .await; + cache + .upsert_ingest(payload_id, response, BackendId::from_index(0), 0.into()) + .await; + }); + }); + + runtime + .block_on(cache.close()) + .expect("close benchmark cache"); +}