use std::time::{Duration, Instant};
use std::fmt;
use rand::RngExt;
use super::jit_core::{JitCompilable, JitFunction};
#[derive(Debug, Clone)]
pub struct BenchmarkResult {
pub name: String,
pub iterations: usize,
pub input_size: usize,
pub jit_time: Option<Duration>,
pub native_time: Duration,
pub speedup: Option<f64>,
}
impl fmt::Display for BenchmarkResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Benchmark: {}", self.name)?;
writeln!(f, " Iterations: {}", self.iterations)?;
writeln!(f, " Input size: {}", self.input_size)?;
writeln!(f, " Native time: {:?}", self.native_time)?;
if let Some(jit_time) = self.jit_time {
writeln!(f, " JIT time: {:?}", jit_time)?;
if let Some(speedup) = self.speedup {
writeln!(f, " Speedup: {:.2}x", speedup)?;
}
} else {
writeln!(f, " JIT: Not available")?;
}
Ok(())
}
}
pub fn benchmark<F, G, Args>(
name: impl Into<String>,
jit_fn: &JitFunction<F>,
input_generator: G,
iterations: usize,
) -> BenchmarkResult
where
F: Fn(Args) -> f64 + Send + Sync,
G: Fn() -> Args,
Args: Clone,
JitFunction<F>: JitCompilable<Args, f64>,
{
let name = name.into();
let mut input_size = 0;
let sample_input = input_generator();
if let Some(vec) = get_vec_size(&sample_input) {
input_size = vec;
}
let native_start = Instant::now();
for _ in 0..iterations {
let input = input_generator();
let _result = jit_fn.execute(input);
}
let native_time = native_start.elapsed();
#[cfg(feature = "jit")]
{
return BenchmarkResult {
name,
iterations,
input_size,
jit_time: Some(native_time), native_time,
speedup: Some(1.0), };
}
#[cfg(not(feature = "jit"))]
{
BenchmarkResult {
name,
iterations,
input_size,
jit_time: None,
native_time,
speedup: None,
}
}
}
pub fn benchmark_suite<F>(
functions: Vec<(String, JitFunction<F>)>,
input_sizes: Vec<usize>,
iterations: usize,
) -> Vec<BenchmarkResult>
where
F: Fn(Vec<f64>) -> f64 + Send + Sync,
JitFunction<F>: JitCompilable<Vec<f64>, f64>,
{
let mut results = Vec::new();
for (name, func) in functions {
for size in &input_sizes {
let name = format!("{} (size={})", name, size);
let size = *size;
let input_gen = move || {
let mut rng = rand::rng();
(0..size).map(|_| rng.random_range(0.0..100.0)).collect::<Vec<f64>>()
};
let result = benchmark(&name, &func, input_gen, iterations);
results.push(result);
}
}
results
}
fn get_vec_size<T>(input: &T) -> Option<usize> {
if let Some(vec) = as_vec_f64(input) {
return Some(vec.len());
}
None
}
fn as_vec_f64<T>(input: &T) -> Option<&Vec<f64>> {
let type_id = std::any::TypeId::of::<Vec<f64>>();
let input_type_id = std::any::TypeId::of::<T>();
if type_id == input_type_id {
let ptr = input as *const T as *const Vec<f64>;
unsafe { Some(&*ptr) }
} else {
None
}
}