use sha2::{Digest, Sha256};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::path::PathBuf;
use std::time::Instant;
pub fn benchmark_allocation() -> f64 {
let start = Instant::now();
let size = 64 * 1024 * 1024; let mut v: Vec<u8> = vec![0; size];
for i in (0..size).step_by(4096) {
v[i] = (i & 0xFF) as u8;
}
std::hint::black_box(&v);
drop(v);
start.elapsed().as_secs_f64() * 1000.0
}
pub fn benchmark_compute() -> f64 {
let start = Instant::now();
let mut hasher = Sha256::new();
let data: Vec<u8> = (0..1_000_000u32)
.map(|i| (i.wrapping_mul(2654435761) & 0xFF) as u8)
.collect();
for _ in 0..10 {
hasher.update(&data);
}
let _ = std::hint::black_box(hasher.finalize());
start.elapsed().as_secs_f64() * 1000.0
}
pub struct IoBenchmarkResult {
pub read_mb_per_sec: f64,
pub write_mb_per_sec: f64,
pub sha_duration_ms: f64,
}
pub fn benchmark_io(test_file: &str, file_size_mb: usize) -> std::io::Result<IoBenchmarkResult> {
let _ = std::fs::write("/proc/sys/vm/drop_caches", b"3");
let file_size = file_size_mb as f64;
let start = Instant::now();
let mut file = File::open(test_file)?;
let mut hasher = Sha256::new();
let mut buffer = vec![0u8; 1024 * 1024];
loop {
let n = file.read(&mut buffer)?;
if n == 0 {
break;
}
hasher.update(&buffer[..n]);
}
let _ = hasher.finalize();
let read_duration = start.elapsed();
let read_mb_per_sec = file_size / read_duration.as_secs_f64();
let sha_duration_ms = read_duration.as_secs_f64() * 1000.0;
let write_test_file = write_benchmark_path(test_file);
let _cleanup = RemoveFileOnDrop {
path: write_test_file.clone(),
};
let write_size_mb = (file_size_mb / 4).max(16);
let start = Instant::now();
{
let mut f = OpenOptions::new()
.write(true)
.create_new(true)
.open(&write_test_file)?;
let chunk = vec![0xCDu8; 1024 * 1024];
for _ in 0..write_size_mb {
f.write_all(&chunk)?;
}
f.sync_all()?; }
let write_duration = start.elapsed();
let write_mb_per_sec = write_size_mb as f64 / write_duration.as_secs_f64();
Ok(IoBenchmarkResult {
read_mb_per_sec,
write_mb_per_sec,
sha_duration_ms,
})
}
pub fn create_test_file(path: &str, size_mb: usize) -> std::io::Result<()> {
eprintln!("Creating {} MB test file at {}...", size_mb, path);
let mut f = File::create(path)?;
let chunk = vec![0xABu8; 1024 * 1024];
for i in 0..size_mb {
f.write_all(&chunk)?;
if i % 64 == 0 {
eprint!("\r Progress: {}%", (i * 100) / size_mb);
}
}
eprintln!("\r Done creating test file. ");
f.sync_all()?;
Ok(())
}
fn write_benchmark_path(test_file: &str) -> PathBuf {
let suffix = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
PathBuf::from(format!(
"{}.write_test.{}.{}",
test_file,
std::process::id(),
suffix
))
}
struct RemoveFileOnDrop {
path: PathBuf,
}
impl Drop for RemoveFileOnDrop {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.path);
}
}
#[cfg(test)]
mod tests {
use super::{create_test_file, write_benchmark_path};
#[test]
fn write_benchmark_path_does_not_target_fixed_sibling_file() {
let path = write_benchmark_path("/tmp/slowtest.bin");
let path = path.to_string_lossy();
assert!(path.starts_with("/tmp/slowtest.bin.write_test."));
assert_ne!(path, "/tmp/slowtest.bin.write_test");
}
#[test]
fn create_test_file_writes_requested_pattern_and_size() {
let path = std::env::temp_dir().join(format!(
"cargo-slow-create-test-file-{}.bin",
std::process::id()
));
let _ = std::fs::remove_file(&path);
create_test_file(path.to_str().unwrap(), 1).unwrap();
let contents = std::fs::read(&path).unwrap();
assert_eq!(contents.len(), 1024 * 1024);
assert!(contents.iter().all(|byte| *byte == 0xAB));
let _ = std::fs::remove_file(&path);
}
}