use std::{
sync::{Arc, Barrier},
thread,
time::{Duration, Instant},
};
struct BenchmarkResult {
runs: u64,
duration: Duration,
}
pub fn benchmark<F>(name: &str, runs: u64, warmup: u64, parallel_count: u64, mut func: F)
where
F: FnMut(),
{
for _ in 0..warmup {
std::hint::black_box(func());
}
let start = Instant::now();
for _ in 0..runs {
std::hint::black_box(func());
}
let duration = start.elapsed();
let result = BenchmarkResult { runs, duration };
let hashes_per_second =
(result.runs as f64 / result.duration.as_secs_f64()) * parallel_count as f64;
print_report(name, hashes_per_second);
}
pub fn benchmark_parallel<F>(
name: &str,
runs: u64,
parallel_count: u64,
thread_count: usize,
mut func: F,
) where
F: FnMut() + Send + Clone + 'static,
{
let barrier = Arc::new(Barrier::new(thread_count));
let mut handles = Vec::with_capacity(thread_count);
let start_time = Instant::now();
for _ in 0..thread_count {
let mut func_clone = func.clone();
let c_barrier = Arc::clone(&barrier);
handles.push(thread::spawn(move || {
c_barrier.wait();
for _ in 0..runs {
func_clone();
}
}));
}
for handle in handles {
handle.join().unwrap();
}
let duration = start_time.elapsed();
let result = BenchmarkResult { runs, duration };
let hashes_per_second = (result.runs as f64 / result.duration.as_secs_f64())
* parallel_count as f64
* thread_count as f64;
print_report(name, hashes_per_second);
}
fn print_report(name: &str, hashes_per_second: f64) {
println!(
"{} ran at {}",
name,
format_hashes_per_second(hashes_per_second)
);
}
fn format_hashes_per_second(hashes_per_second: f64) -> String {
let gh_delimiter = 1_000_000_000.0;
let mh_delimiter = 1_000_000.0;
let kh_delimiter = 1_000.0;
if hashes_per_second > gh_delimiter {
format!("{:.2}GH/s", hashes_per_second / gh_delimiter)
} else if hashes_per_second > mh_delimiter {
format!("{:.2}MH/s", hashes_per_second / mh_delimiter)
} else if hashes_per_second > kh_delimiter {
format!("{:.2}KH/s", hashes_per_second / kh_delimiter)
} else {
format!("{:.2}H/s", hashes_per_second)
}
}