lines_read was incorrectly set to max_lines.min(total) (the loop upper bound) instead of the actual number of successfully read lines. Now tracks lines_read via get_line(i).is_some() counter and uses max_lines.min(total) as the correct loop upper bound to handle empty file edge case. Fixes #43
238 lines
7.2 KiB
Rust
238 lines
7.2 KiB
Rust
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<BenchmarkResult> {
|
|
let mut results = Vec::new();
|
|
|
|
results.extend(bench_idle_rss::<MmapReaderPlain>("mmap", "plain", config));
|
|
results.extend(bench_idle_rss::<MmapReaderSequential>(
|
|
"mmap",
|
|
"sequential",
|
|
config,
|
|
));
|
|
results.extend(bench_idle_rss::<MmapReaderRandom>("mmap", "random", config));
|
|
results.extend(bench_idle_rss::<MmapReaderPopulate>(
|
|
"mmap", "populate", config,
|
|
));
|
|
results.extend(bench_idle_rss::<MmapReaderPhaseAware>(
|
|
"mmap",
|
|
"phase_aware",
|
|
config,
|
|
));
|
|
results.extend(bench_idle_rss::<PreadReaderPlain>("pread", "plain", config));
|
|
results.extend(bench_idle_rss::<PreadReaderRandom>(
|
|
"pread", "random", config,
|
|
));
|
|
results.extend(bench_idle_rss::<PreadReaderSequential>(
|
|
"pread",
|
|
"sequential",
|
|
config,
|
|
));
|
|
|
|
results.extend(bench_scroll_rss::<MmapReaderPlain>("mmap", "plain", config));
|
|
results.extend(bench_scroll_rss::<PreadReaderPlain>(
|
|
"pread", "plain", config,
|
|
));
|
|
|
|
results.extend(bench_jump_end_rss::<MmapReaderPlain>(
|
|
"mmap", "plain", config,
|
|
));
|
|
results.extend(bench_jump_end_rss::<PreadReaderPlain>(
|
|
"pread", "plain", config,
|
|
));
|
|
|
|
results.extend(bench_rss_reclaim::<MmapReaderPlain>(
|
|
"mmap", "plain", config,
|
|
));
|
|
results.extend(bench_rss_reclaim::<PreadReaderPlain>(
|
|
"pread", "plain", config,
|
|
));
|
|
|
|
results
|
|
}
|
|
|
|
fn bench_idle_rss<B: FileReaderBackend>(
|
|
backend: &str,
|
|
variant: &str,
|
|
config: &BenchConfig,
|
|
) -> Vec<BenchmarkResult> {
|
|
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<B: FileReaderBackend>(
|
|
backend: &str,
|
|
variant: &str,
|
|
config: &BenchConfig,
|
|
) -> Vec<BenchmarkResult> {
|
|
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<B: FileReaderBackend>(
|
|
backend: &str,
|
|
variant: &str,
|
|
config: &BenchConfig,
|
|
) -> Vec<BenchmarkResult> {
|
|
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<B: FileReaderBackend>(
|
|
backend: &str,
|
|
variant: &str,
|
|
config: &BenchConfig,
|
|
) -> Vec<BenchmarkResult> {
|
|
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,
|
|
}]
|
|
}
|