fix(bench): eliminate SIGBUS handler static mut UB with Once + raw atomics (closes #33)
Replace `static mut OLD_SIGBUS_HANDLER` with AtomicU8 + AtomicPtr to remove data race UB when concurrent benchmarks call open() from multiple threads. Key changes: - Use `Once::call_once` to guarantee single handler installation - Publish old handler to atomics BEFORE installing new handler (closes the handler-active-but-state-unpublished race window) - Read atomics with Acquire in signal handler (async-signal-safe) - Align si_addr to page boundary before mmap(MAP_FIXED) - Add concurrent test: 8 threads open all 5 variants simultaneously
This commit is contained in:
129
crates/bench/src/suites/startup.rs
Normal file
129
crates/bench/src/suites/startup.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
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_hot_open::<MmapReaderPlain>("mmap", "plain", config));
|
||||
results.extend(bench_hot_open::<MmapReaderSequential>(
|
||||
"mmap",
|
||||
"sequential",
|
||||
config,
|
||||
));
|
||||
results.extend(bench_hot_open::<MmapReaderRandom>("mmap", "random", config));
|
||||
results.extend(bench_hot_open::<MmapReaderPopulate>(
|
||||
"mmap", "populate", config,
|
||||
));
|
||||
results.extend(bench_hot_open::<MmapReaderPhaseAware>(
|
||||
"mmap",
|
||||
"phase_aware",
|
||||
config,
|
||||
));
|
||||
results.extend(bench_hot_open::<PreadReaderPlain>("pread", "plain", config));
|
||||
results.extend(bench_hot_open::<PreadReaderRandom>(
|
||||
"pread", "random", config,
|
||||
));
|
||||
results.extend(bench_hot_open::<PreadReaderSequential>(
|
||||
"pread",
|
||||
"sequential",
|
||||
config,
|
||||
));
|
||||
|
||||
if !config.quick_mode {
|
||||
if MetricsCollector::clear_file_cache(&config.test_file).is_ok() {
|
||||
results.extend(bench_cold_open::<MmapReaderPlain>("mmap", "plain", config));
|
||||
results.extend(bench_cold_open::<MmapReaderSequential>(
|
||||
"mmap",
|
||||
"sequential",
|
||||
config,
|
||||
));
|
||||
results.extend(bench_cold_open::<MmapReaderRandom>(
|
||||
"mmap", "random", config,
|
||||
));
|
||||
results.extend(bench_cold_open::<MmapReaderPopulate>(
|
||||
"mmap", "populate", config,
|
||||
));
|
||||
results.extend(bench_cold_open::<MmapReaderPhaseAware>(
|
||||
"mmap",
|
||||
"phase_aware",
|
||||
config,
|
||||
));
|
||||
results.extend(bench_cold_open::<PreadReaderPlain>(
|
||||
"pread", "plain", config,
|
||||
));
|
||||
results.extend(bench_cold_open::<PreadReaderRandom>(
|
||||
"pread", "random", config,
|
||||
));
|
||||
results.extend(bench_cold_open::<PreadReaderSequential>(
|
||||
"pread",
|
||||
"sequential",
|
||||
config,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
fn bench_hot_open<B: FileReaderBackend>(
|
||||
backend: &str,
|
||||
variant: &str,
|
||||
config: &BenchConfig,
|
||||
) -> Vec<BenchmarkResult> {
|
||||
open_and_measure::<B>(backend, variant, &config.test_file, "hot_open")
|
||||
}
|
||||
|
||||
fn bench_cold_open<B: FileReaderBackend>(
|
||||
backend: &str,
|
||||
variant: &str,
|
||||
config: &BenchConfig,
|
||||
) -> Vec<BenchmarkResult> {
|
||||
let _ = MetricsCollector::clear_file_cache(&config.test_file);
|
||||
open_and_measure::<B>(backend, variant, &config.test_file, "cold_open")
|
||||
}
|
||||
|
||||
fn open_and_measure<B: FileReaderBackend>(
|
||||
backend: &str,
|
||||
variant: &str,
|
||||
path: &Path,
|
||||
test_name: &str,
|
||||
) -> Vec<BenchmarkResult> {
|
||||
let start = std::time::Instant::now();
|
||||
let reader = B::open(path).expect("Failed to open file");
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
let rss = MetricsCollector::read_rss();
|
||||
let faults = MetricsCollector::read_page_faults();
|
||||
|
||||
let result = BenchmarkResult {
|
||||
category: "startup".into(),
|
||||
test_name: test_name.into(),
|
||||
backend: backend.into(),
|
||||
variant: variant.into(),
|
||||
latency_us: vec![elapsed.as_micros() as u64],
|
||||
rss_kb: rss.vm_rss_kb,
|
||||
rss_peak_kb: rss.vm_hwm_kb,
|
||||
page_faults: faults.minor_faults + faults.major_faults,
|
||||
extra: {
|
||||
let mut m = HashMap::new();
|
||||
m.insert("total_lines".into(), reader.total_lines() as f64);
|
||||
m.insert(
|
||||
"file_size_mb".into(),
|
||||
reader.file_size() as f64 / (1024.0 * 1024.0),
|
||||
);
|
||||
m
|
||||
},
|
||||
};
|
||||
reader.close();
|
||||
vec![result]
|
||||
}
|
||||
Reference in New Issue
Block a user