use std::{
env,
ffi::CString,
fs::{self, File},
path::{Path, PathBuf},
time::SystemTime,
};
use brunch::{benches, Bench};
use libc::{close, open, O_RDONLY, O_RDWR, O_WRONLY};
use nix::unistd::unlink;
struct XorShift32 {
state: u32,
}
impl XorShift32 {
fn new(seed: u32) -> Self {
Self { state: seed }
}
fn next_u32(&mut self) -> u32 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
self.state = x;
x
}
}
fn create_temp_files(count: usize) -> (PathBuf, Vec<PathBuf>) {
let mut dir = env::temp_dir();
let unique = format!(
"syd_open_bench_{}_{}",
count,
SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
);
dir.push(unique);
fs::create_dir_all(&dir).unwrap_or_else(|_| panic!("Failed to create directory: {:?}", &dir));
let mut paths = Vec::with_capacity(count);
for i in 0..count {
let file_path = dir.join(format!("file_{}", i));
File::create(&file_path)
.unwrap_or_else(|_| panic!("Failed to create file: {:?}", &file_path));
paths.push(file_path);
}
(dir, paths)
}
fn open_random_file(files: &[PathBuf], mode: i32, rng: &mut XorShift32) {
let chosen = (rng.next_u32() as usize) % files.len();
let c_path = CString::new(files[chosen].to_string_lossy().as_bytes())
.expect("Failed to convert path to CString");
let fd = unsafe { open(c_path.as_ptr(), mode) };
if fd < 0 {
panic!("open() failed for {:?}", files[chosen]);
}
unsafe {
close(fd);
}
}
fn bench_open(files: &[PathBuf], mode: i32) {
let mut rng = XorShift32::new(1);
open_random_file(files, mode, &mut rng);
}
fn cleanup_temp_dir(dir: &Path, files: &[PathBuf]) {
for f in files {
let _ = unlink(f);
}
let _ = fs::remove_dir_all(dir);
}
fn main() {
let file_counts = [1, 8, 64, 128];
let mut setups = Vec::new();
for &count in &file_counts {
let (dir, files) = create_temp_files(count);
setups.push((count, dir, files));
}
benches!(
inline:
Bench::new("Open(O_RDONLY) file_count=1").run(|| {
bench_open(&setups[0].2, O_RDONLY);
}),
Bench::new("Open(O_RDONLY) file_count=8").run(|| {
bench_open(&setups[1].2, O_RDONLY);
}),
Bench::new("Open(O_RDONLY) file_count=64").run(|| {
bench_open(&setups[2].2, O_RDONLY);
}),
Bench::new("Open(O_RDONLY) file_count=128").run(|| {
bench_open(&setups[3].2, O_RDONLY);
}),
Bench::new("Open(O_WRONLY) file_count=1").run(|| {
bench_open(&setups[0].2, O_WRONLY);
}),
Bench::new("Open(O_WRONLY) file_count=8").run(|| {
bench_open(&setups[1].2, O_WRONLY);
}),
Bench::new("Open(O_WRONLY) file_count=64").run(|| {
bench_open(&setups[2].2, O_WRONLY);
}),
Bench::new("Open(O_WRONLY) file_count=128").run(|| {
bench_open(&setups[3].2, O_WRONLY);
}),
Bench::new("Open(O_RDWR) file_count=1").run(|| {
bench_open(&setups[0].2, O_RDWR);
}),
Bench::new("Open(O_RDWR) file_count=8").run(|| {
bench_open(&setups[1].2, O_RDWR);
}),
Bench::new("Open(O_RDWR) file_count=64").run(|| {
bench_open(&setups[2].2, O_RDWR);
}),
Bench::new("Open(O_RDWR) file_count=128").run(|| {
bench_open(&setups[3].2, O_RDWR);
}),
);
for (_, dir, files) in setups {
cleanup_temp_dir(&dir, &files);
}
}