Compare commits
3 Commits
e945a357f7
...
502479677b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
502479677b | ||
|
|
5656b26d7b | ||
|
|
a8b64e78bd |
@@ -3,6 +3,10 @@ name = "log-viewer-bench"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "log_viewer_bench"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "log-viewer-bench"
|
name = "log-viewer-bench"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|||||||
@@ -21,15 +21,7 @@ pub fn ensure_test_file(path: &Path) -> std::io::Result<TestFileInfo> {
|
|||||||
fn get_file_info(path: &Path) -> std::io::Result<TestFileInfo> {
|
fn get_file_info(path: &Path) -> std::io::Result<TestFileInfo> {
|
||||||
let metadata = fs::metadata(path)?;
|
let metadata = fs::metadata(path)?;
|
||||||
let size_bytes = metadata.len();
|
let size_bytes = metadata.len();
|
||||||
|
let line_count = count_existing_lines(path)?;
|
||||||
let file = fs::File::open(path)?;
|
|
||||||
let mut reader = BufReader::new(file);
|
|
||||||
let mut line_count: u64 = 0;
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
while reader.read_until(b'\n', &mut buf)? > 0 {
|
|
||||||
line_count += 1;
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
let avg_line_length = if line_count > 0 {
|
let avg_line_length = if line_count > 0 {
|
||||||
size_bytes as f64 / line_count as f64
|
size_bytes as f64 / line_count as f64
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ pub fn format_report(results: &[BenchmarkResult]) -> String {
|
|||||||
|
|
||||||
let mut variants: Vec<(String, String)> = Vec::new();
|
let mut variants: Vec<(String, String)> = Vec::new();
|
||||||
for r in category_results {
|
for r in category_results {
|
||||||
let key = format!("{} ({})", r.backend, r.variant);
|
|
||||||
if !variants
|
if !variants
|
||||||
.iter()
|
.iter()
|
||||||
.any(|(b, v)| format!("{} ({})", b, v) == key)
|
.any(|(b, v)| b == &r.backend && v == &r.variant)
|
||||||
{
|
{
|
||||||
variants.push((r.backend.clone(), r.variant.clone()));
|
variants.push((r.backend.clone(), r.variant.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
variants.sort();
|
||||||
|
|
||||||
report.push_str("### Latency\n\n");
|
report.push_str("### Latency\n\n");
|
||||||
report.push_str("| Test |");
|
report.push_str("| Test |");
|
||||||
@@ -143,7 +143,13 @@ pub fn format_report(results: &[BenchmarkResult]) -> String {
|
|||||||
report.push_str("| Test | Variant | RSS | Peak RSS | Page Faults |\n");
|
report.push_str("| Test | Variant | RSS | Peak RSS | Page Faults |\n");
|
||||||
report.push_str("|------|---------|-----|----------|-------------|\n");
|
report.push_str("|------|---------|-----|----------|-------------|\n");
|
||||||
|
|
||||||
for r in category_results {
|
let mut mem_rows: Vec<&BenchmarkResult> =
|
||||||
|
category_results.iter().copied().collect();
|
||||||
|
mem_rows.sort_by(|a, b| {
|
||||||
|
(&a.test_name, &a.backend, &a.variant)
|
||||||
|
.cmp(&(&b.test_name, &b.backend, &b.variant))
|
||||||
|
});
|
||||||
|
for r in mem_rows {
|
||||||
let variant_label = format!("{} ({})", r.backend, r.variant);
|
let variant_label = format!("{} ({})", r.backend, r.variant);
|
||||||
report.push_str(&format!(
|
report.push_str(&format!(
|
||||||
"| {} | {} | {} | {} | {} |\n",
|
"| {} | {} | {} | {} | {} |\n",
|
||||||
@@ -157,7 +163,7 @@ pub fn format_report(results: &[BenchmarkResult]) -> String {
|
|||||||
report.push('\n');
|
report.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
let extras: Vec<(String, String, Vec<(String, f64)>)> = category_results
|
let mut extras: Vec<(String, String, Vec<(String, f64)>)> = category_results
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|r| !r.extra.is_empty())
|
.filter(|r| !r.extra.is_empty())
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
@@ -171,6 +177,7 @@ pub fn format_report(results: &[BenchmarkResult]) -> String {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
extras.sort_by(|a, b| (&a.0, &a.1).cmp(&(&b.0, &b.1)));
|
||||||
|
|
||||||
if !extras.is_empty() {
|
if !extras.is_empty() {
|
||||||
report.push_str("### Extra Metrics\n\n");
|
report.push_str("### Extra Metrics\n\n");
|
||||||
@@ -205,3 +212,50 @@ fn capitalize(s: &str) -> String {
|
|||||||
Some(f) => f.to_uppercase().chain(c).collect(),
|
Some(f) => f.to_uppercase().chain(c).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn make_result(
|
||||||
|
category: &str,
|
||||||
|
test_name: &str,
|
||||||
|
backend: &str,
|
||||||
|
variant: &str,
|
||||||
|
latency_us: Vec<u64>,
|
||||||
|
) -> BenchmarkResult {
|
||||||
|
BenchmarkResult {
|
||||||
|
category: category.to_string(),
|
||||||
|
test_name: test_name.to_string(),
|
||||||
|
backend: backend.to_string(),
|
||||||
|
variant: variant.to_string(),
|
||||||
|
latency_us,
|
||||||
|
rss_kb: 0,
|
||||||
|
rss_peak_kb: 0,
|
||||||
|
page_faults: 0,
|
||||||
|
extra: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn report_ordering_independent_of_input_order() {
|
||||||
|
let set_a = vec![
|
||||||
|
make_result("sequential", "read_1mb", "pread", "default", vec![100, 110, 105]),
|
||||||
|
make_result("sequential", "read_1mb", "mmap", "default", vec![80, 85, 90]),
|
||||||
|
make_result("sequential", "read_4kb", "pread", "default", vec![10, 12, 11]),
|
||||||
|
make_result("sequential", "read_4kb", "mmap", "default", vec![8, 9, 7]),
|
||||||
|
];
|
||||||
|
|
||||||
|
let set_b = vec![
|
||||||
|
make_result("sequential", "read_4kb", "mmap", "default", vec![8, 9, 7]),
|
||||||
|
make_result("sequential", "read_1mb", "mmap", "default", vec![80, 85, 90]),
|
||||||
|
make_result("sequential", "read_4kb", "pread", "default", vec![10, 12, 11]),
|
||||||
|
make_result("sequential", "read_1mb", "pread", "default", vec![100, 110, 105]),
|
||||||
|
];
|
||||||
|
|
||||||
|
let report_a = format_report(&set_a);
|
||||||
|
let report_b = format_report(&set_b);
|
||||||
|
assert_eq!(report_a, report_b, "Reports must be identical regardless of input order");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,35 +40,42 @@ pub fn run(config: &BenchConfig) -> Vec<BenchmarkResult> {
|
|||||||
));
|
));
|
||||||
|
|
||||||
if !config.quick_mode {
|
if !config.quick_mode {
|
||||||
if MetricsCollector::clear_file_cache(&config.test_file).is_ok() {
|
match MetricsCollector::clear_file_cache(&config.test_file) {
|
||||||
results.extend(bench_cold_open::<MmapReaderPlain>("mmap", "plain", config));
|
Ok(()) => {
|
||||||
results.extend(bench_cold_open::<MmapReaderSequential>(
|
results.extend(bench_cold_open::<MmapReaderPlain>("mmap", "plain", config));
|
||||||
"mmap",
|
results.extend(bench_cold_open::<MmapReaderSequential>(
|
||||||
"sequential",
|
"mmap",
|
||||||
config,
|
"sequential",
|
||||||
));
|
config,
|
||||||
results.extend(bench_cold_open::<MmapReaderRandom>(
|
));
|
||||||
"mmap", "random", config,
|
results.extend(bench_cold_open::<MmapReaderRandom>(
|
||||||
));
|
"mmap", "random", config,
|
||||||
results.extend(bench_cold_open::<MmapReaderPopulate>(
|
));
|
||||||
"mmap", "populate", config,
|
results.extend(bench_cold_open::<MmapReaderPopulate>(
|
||||||
));
|
"mmap", "populate", config,
|
||||||
results.extend(bench_cold_open::<MmapReaderPhaseAware>(
|
));
|
||||||
"mmap",
|
results.extend(bench_cold_open::<MmapReaderPhaseAware>(
|
||||||
"phase_aware",
|
"mmap",
|
||||||
config,
|
"phase_aware",
|
||||||
));
|
config,
|
||||||
results.extend(bench_cold_open::<PreadReaderPlain>(
|
));
|
||||||
"pread", "plain", config,
|
results.extend(bench_cold_open::<PreadReaderPlain>(
|
||||||
));
|
"pread", "plain", config,
|
||||||
results.extend(bench_cold_open::<PreadReaderRandom>(
|
));
|
||||||
"pread", "random", config,
|
results.extend(bench_cold_open::<PreadReaderRandom>(
|
||||||
));
|
"pread", "random", config,
|
||||||
results.extend(bench_cold_open::<PreadReaderSequential>(
|
));
|
||||||
"pread",
|
results.extend(bench_cold_open::<PreadReaderSequential>(
|
||||||
"sequential",
|
"pread",
|
||||||
config,
|
"sequential",
|
||||||
));
|
config,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(
|
||||||
|
"WARNING: Failed to clear file cache; skipping cold startup benchmarks because results would not be cold: {e}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +95,15 @@ fn bench_cold_open<B: FileReaderBackend>(
|
|||||||
variant: &str,
|
variant: &str,
|
||||||
config: &BenchConfig,
|
config: &BenchConfig,
|
||||||
) -> Vec<BenchmarkResult> {
|
) -> Vec<BenchmarkResult> {
|
||||||
let _ = MetricsCollector::clear_file_cache(&config.test_file);
|
match MetricsCollector::clear_file_cache(&config.test_file) {
|
||||||
open_and_measure::<B>(backend, variant, &config.test_file, "cold_open")
|
Ok(()) => open_and_measure::<B>(backend, variant, &config.test_file, "cold_open"),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(
|
||||||
|
"WARNING: Failed to clear file cache for {backend}/{variant}; skipping cold benchmark: {e}"
|
||||||
|
);
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_and_measure<B: FileReaderBackend>(
|
fn open_and_measure<B: FileReaderBackend>(
|
||||||
|
|||||||
Reference in New Issue
Block a user