use std::collections::HashMap; use crate::metrics::MetricsCollector; use crate::mmap_reader::{ MmapReaderPhaseAware, MmapReaderPlain, MmapReaderPopulate, MmapReaderRandom, MmapReaderSequential, }; use crate::pread_reader::{PreadReaderPlain, PreadReaderRandom, PreadReaderSequential}; use crate::runner::BenchConfig; use crate::types::BenchmarkResult; use crate::FileReaderBackend; pub fn run(config: &BenchConfig) -> Vec { let mut results = Vec::new(); results.extend(bench_idle_rss::("mmap", "plain", config)); results.extend(bench_idle_rss::( "mmap", "sequential", config, )); results.extend(bench_idle_rss::("mmap", "random", config)); results.extend(bench_idle_rss::( "mmap", "populate", config, )); results.extend(bench_idle_rss::( "mmap", "phase_aware", config, )); results.extend(bench_idle_rss::("pread", "plain", config)); results.extend(bench_idle_rss::( "pread", "random", config, )); results.extend(bench_idle_rss::( "pread", "sequential", config, )); results.extend(bench_scroll_rss::("mmap", "plain", config)); results.extend(bench_scroll_rss::( "pread", "plain", config, )); results.extend(bench_jump_end_rss::( "mmap", "plain", config, )); results.extend(bench_jump_end_rss::( "pread", "plain", config, )); results.extend(bench_rss_reclaim::( "mmap", "plain", config, )); results.extend(bench_rss_reclaim::( "pread", "plain", config, )); results } fn bench_idle_rss( backend: &str, variant: &str, config: &BenchConfig, ) -> Vec { let reader = B::open(&config.test_file).expect("Failed to open file"); let rss = MetricsCollector::read_rss(); let faults = MetricsCollector::read_page_faults(); let mut extra = HashMap::new(); extra.insert("total_lines".into(), reader.total_lines() as f64); extra.insert( "file_size_mb".into(), reader.file_size() as f64 / (1024.0 * 1024.0), ); reader.close(); vec![BenchmarkResult { category: "memory".into(), test_name: "idle_rss".into(), backend: backend.into(), variant: variant.into(), latency_us: vec![], rss_kb: rss.vm_rss_kb, rss_peak_kb: rss.vm_hwm_kb, page_faults: faults.minor_faults + faults.major_faults, extra, }] } fn bench_scroll_rss( backend: &str, variant: &str, config: &BenchConfig, ) -> Vec { let reader = B::open(&config.test_file).expect("Failed to open file"); let total = reader.total_lines(); let sample_interval = 100_000; let max_lines = if config.quick_mode { 100_000 } else { total }; let upper = max_lines.min(total); let mut rss_samples = Vec::new(); let mut hwm_samples = Vec::new(); let mut lines_read = 0usize; for i in (0..upper).step_by(sample_interval) { if reader.get_line(i).is_some() { lines_read += 1; } let rss = MetricsCollector::read_rss(); rss_samples.push(rss.vm_rss_kb); hwm_samples.push(rss.vm_hwm_kb); } let final_rss = MetricsCollector::read_rss(); let faults = MetricsCollector::read_page_faults(); let mut extra = HashMap::new(); extra.insert("rss_samples_count".into(), rss_samples.len() as f64); extra.insert( "max_rss_kb".into(), rss_samples.iter().copied().fold(0u64, u64::max) as f64, ); extra.insert( "max_hwm_kb".into(), hwm_samples.iter().copied().fold(0u64, u64::max) as f64, ); extra.insert("lines_read".into(), lines_read as f64); reader.close(); vec![BenchmarkResult { category: "memory".into(), test_name: "scroll_rss".into(), backend: backend.into(), variant: variant.into(), latency_us: vec![], rss_kb: final_rss.vm_rss_kb, rss_peak_kb: final_rss.vm_hwm_kb, page_faults: faults.minor_faults + faults.major_faults, extra, }] } fn bench_jump_end_rss( backend: &str, variant: &str, config: &BenchConfig, ) -> Vec { let reader = B::open(&config.test_file).expect("Failed to open file"); let total = reader.total_lines(); let last_line = total.saturating_sub(1); let _ = reader.get_line(last_line); let rss = MetricsCollector::read_rss(); let faults = MetricsCollector::read_page_faults(); let mut extra = HashMap::new(); extra.insert("last_line_idx".into(), last_line as f64); extra.insert("total_lines".into(), total as f64); reader.close(); vec![BenchmarkResult { category: "memory".into(), test_name: "jump_end_rss".into(), backend: backend.into(), variant: variant.into(), latency_us: vec![], rss_kb: rss.vm_rss_kb, rss_peak_kb: rss.vm_hwm_kb, page_faults: faults.minor_faults + faults.major_faults, extra, }] } fn bench_rss_reclaim( backend: &str, variant: &str, config: &BenchConfig, ) -> Vec { let reader = B::open(&config.test_file).expect("Failed to open file"); let total = reader.total_lines(); let last_line = total.saturating_sub(1); let _ = reader.get_line(last_line); let wait_secs: u64 = if config.quick_mode { 5 } else { 30 }; let sample_interval: u64 = 5; let num_samples = (wait_secs / sample_interval) as usize; let mut rss_samples = Vec::with_capacity(num_samples); let mut hwm_samples = Vec::with_capacity(num_samples); for _ in 0..num_samples { std::thread::sleep(std::time::Duration::from_secs(sample_interval)); let rss = MetricsCollector::read_rss(); rss_samples.push(rss.vm_rss_kb); hwm_samples.push(rss.vm_hwm_kb); } let final_rss = MetricsCollector::read_rss(); let faults = MetricsCollector::read_page_faults(); reader.close(); let mut extra = HashMap::new(); extra.insert("wait_total_secs".into(), wait_secs as f64); extra.insert("rss_samples".into(), rss_samples.len() as f64); if let (Some(&first), Some(&last)) = (rss_samples.first(), rss_samples.last()) { extra.insert("rss_first_kb".into(), first as f64); extra.insert("rss_last_kb".into(), last as f64); extra.insert( "rss_change_pct".into(), if first > 0 { ((last as f64 - first as f64) / first as f64) * 100.0 } else { 0.0 }, ); } vec![BenchmarkResult { category: "memory".into(), test_name: "rss_reclaim".into(), backend: backend.into(), variant: variant.into(), latency_us: vec![], rss_kb: final_rss.vm_rss_kb, rss_peak_kb: final_rss.vm_hwm_kb, page_faults: faults.minor_faults + faults.major_faults, extra, }] }