use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use numrs2::array::Array;
use numrs2::math;
use std::hint::black_box;
const SIZES: [usize; 4] = [64, 1_000, 10_000, 1_000_000];
const MINMAX_SIZES: [usize; 5] = [64, 1_000, 10_000, 80_000, 1_000_000];
fn f64_arr(n: usize) -> Array<f64> {
Array::from_vec(
(0..n)
.map(|i| ((i.wrapping_mul(2_654_435_761)) % 100_000) as f64 * 0.001 - 50.0)
.collect(),
)
}
fn f32_arr(n: usize) -> Array<f32> {
Array::from_vec(
(0..n)
.map(|i| ((i.wrapping_mul(2_654_435_761)) % 100_000) as f32 * 0.001 - 50.0)
.collect(),
)
}
mod before {
use super::Array;
pub fn sum_f64(arr: &Array<f64>) -> f64 {
let data = arr.to_vec();
let v = data.iter().fold(0.0_f64, |acc, &x| acc + x);
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn sum_f32(arr: &Array<f32>) -> f32 {
let data = arr.to_vec();
let v = data.iter().fold(0.0_f32, |acc, &x| acc + x);
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn mean_f64(arr: &Array<f64>) -> f64 {
let data = arr.to_vec();
let sum = data.iter().fold(0.0_f64, |acc, &x| acc + x);
let v = sum / data.len() as f64;
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn mean_f32(arr: &Array<f32>) -> f32 {
let data = arr.to_vec();
let sum = data.iter().fold(0.0_f32, |acc, &x| acc + x);
let v = sum / data.len() as f32;
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn var_f64(arr: &Array<f64>, ddof: usize) -> f64 {
let data = arr.to_vec();
let n = data.len();
let mean = data.iter().fold(0.0_f64, |acc, &x| acc + x) / n as f64;
let ssd: f64 = data.iter().map(|&x| (x - mean) * (x - mean)).sum();
let v = ssd / (n - ddof) as f64;
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn var_f32(arr: &Array<f32>, ddof: usize) -> f32 {
let data = arr.to_vec();
let n = data.len();
let mean = data.iter().fold(0.0_f32, |acc, &x| acc + x) / n as f32;
let ssd: f32 = data.iter().map(|&x| (x - mean) * (x - mean)).sum();
let v = ssd / (n - ddof) as f32;
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn std_f64(arr: &Array<f64>, ddof: usize) -> f64 {
var_f64(arr, ddof).sqrt()
}
pub fn std_f32(arr: &Array<f32>, ddof: usize) -> f32 {
var_f32(arr, ddof).sqrt()
}
pub fn min_f64(arr: &Array<f64>) -> f64 {
let data = arr.to_vec();
let v = data
.iter()
.skip(1)
.fold(data[0], |m, &x| if x < m { x } else { m });
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn max_f64(arr: &Array<f64>) -> f64 {
let data = arr.to_vec();
let v = data
.iter()
.skip(1)
.fold(data[0], |m, &x| if x > m { x } else { m });
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn min_f32(arr: &Array<f32>) -> f32 {
let data = arr.to_vec();
let v = data
.iter()
.skip(1)
.fold(data[0], |m, &x| if x < m { x } else { m });
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn max_f32(arr: &Array<f32>) -> f32 {
let data = arr.to_vec();
let v = data
.iter()
.skip(1)
.fold(data[0], |m, &x| if x > m { x } else { m });
Array::from_vec(vec![v]).to_vec()[0]
}
pub fn sum_axis1_f64(arr: &Array<f64>, rows: usize, cols: usize) -> Vec<f64> {
let mut result = vec![0.0f64; rows];
for (r, out) in result.iter_mut().enumerate() {
let mut indices = vec![0usize; 2];
indices[0] = r;
let mut sum = 0.0f64;
for c in 0..cols {
indices[1] = c;
sum += arr.get(&indices).expect("index should be in bounds");
}
*out = sum;
}
result
}
}
mod after {
use super::{math, Array};
pub fn sum_f64(arr: &Array<f64>) -> f64 {
math::sum(arr, None, false)
.expect("sum should succeed")
.to_vec()[0]
}
pub fn sum_f32(arr: &Array<f32>) -> f32 {
math::sum(arr, None, false)
.expect("sum should succeed")
.to_vec()[0]
}
pub fn mean_f64(arr: &Array<f64>) -> f64 {
math::mean(arr, None, false)
.expect("mean should succeed")
.to_vec()[0]
}
pub fn mean_f32(arr: &Array<f32>) -> f32 {
math::mean(arr, None, false)
.expect("mean should succeed")
.to_vec()[0]
}
pub fn var_f64(arr: &Array<f64>, ddof: usize) -> f64 {
math::var(arr, None, ddof, false)
.expect("var should succeed")
.to_vec()[0]
}
pub fn var_f32(arr: &Array<f32>, ddof: usize) -> f32 {
math::var(arr, None, ddof, false)
.expect("var should succeed")
.to_vec()[0]
}
pub fn std_f64(arr: &Array<f64>, ddof: usize) -> f64 {
math::std(arr, None, ddof, false)
.expect("std should succeed")
.to_vec()[0]
}
pub fn std_f32(arr: &Array<f32>, ddof: usize) -> f32 {
math::std(arr, None, ddof, false)
.expect("std should succeed")
.to_vec()[0]
}
pub fn min_f64(arr: &Array<f64>) -> f64 {
math::min(arr, None, false)
.expect("min should succeed")
.to_vec()[0]
}
pub fn max_f64(arr: &Array<f64>) -> f64 {
math::max(arr, None, false)
.expect("max should succeed")
.to_vec()[0]
}
pub fn min_f32(arr: &Array<f32>) -> f32 {
math::min(arr, None, false)
.expect("min should succeed")
.to_vec()[0]
}
pub fn max_f32(arr: &Array<f32>) -> f32 {
math::max(arr, None, false)
.expect("max should succeed")
.to_vec()[0]
}
pub fn sum_axis1_f64(arr: &Array<f64>) -> Vec<f64> {
math::sum(arr, Some(1), false)
.expect("sum should succeed")
.to_vec()
}
}
macro_rules! bench_pair {
($group:expr, $n:expr, $label64:literal, $label32:literal, $arr64:expr, $arr32:expr, $before64:expr, $after64:expr, $before32:expr, $after32:expr) => {
$group.bench_with_input(
BenchmarkId::new(concat!("before_", $label64), $n),
&$arr64,
|b, a| b.iter(|| black_box($before64(black_box(a)))),
);
$group.bench_with_input(
BenchmarkId::new(concat!("after_", $label64), $n),
&$arr64,
|b, a| b.iter(|| black_box($after64(black_box(a)))),
);
$group.bench_with_input(
BenchmarkId::new(concat!("before_", $label32), $n),
&$arr32,
|b, a| b.iter(|| black_box($before32(black_box(a)))),
);
$group.bench_with_input(
BenchmarkId::new(concat!("after_", $label32), $n),
&$arr32,
|b, a| b.iter(|| black_box($after32(black_box(a)))),
);
};
}
fn bench_sum(c: &mut Criterion) {
let mut group = c.benchmark_group("reduction_dispatch/sum");
for &n in &SIZES {
group.throughput(Throughput::Elements(n as u64));
let a64 = f64_arr(n);
let a32 = f32_arr(n);
bench_pair!(
group,
n,
"f64",
"f32",
a64,
a32,
before::sum_f64,
after::sum_f64,
before::sum_f32,
after::sum_f32
);
}
group.finish();
}
fn bench_mean(c: &mut Criterion) {
let mut group = c.benchmark_group("reduction_dispatch/mean");
for &n in &SIZES {
group.throughput(Throughput::Elements(n as u64));
let a64 = f64_arr(n);
let a32 = f32_arr(n);
bench_pair!(
group,
n,
"f64",
"f32",
a64,
a32,
before::mean_f64,
after::mean_f64,
before::mean_f32,
after::mean_f32
);
}
group.finish();
}
fn bench_var(c: &mut Criterion) {
let mut group = c.benchmark_group("reduction_dispatch/var");
for &n in &SIZES {
group.throughput(Throughput::Elements(n as u64));
let a64 = f64_arr(n);
let a32 = f32_arr(n);
group.bench_with_input(BenchmarkId::new("before_f64", n), &a64, |b, a| {
b.iter(|| black_box(before::var_f64(black_box(a), 0)))
});
group.bench_with_input(BenchmarkId::new("after_f64", n), &a64, |b, a| {
b.iter(|| black_box(after::var_f64(black_box(a), 0)))
});
group.bench_with_input(BenchmarkId::new("before_f32", n), &a32, |b, a| {
b.iter(|| black_box(before::var_f32(black_box(a), 0)))
});
group.bench_with_input(BenchmarkId::new("after_f32", n), &a32, |b, a| {
b.iter(|| black_box(after::var_f32(black_box(a), 0)))
});
}
group.finish();
}
fn bench_var_10k_threshold(c: &mut Criterion) {
let mut group = c.benchmark_group("reduction_dispatch/var_10k_threshold");
for &n in &[9_999usize, 10_000, 20_000, 40_000] {
group.throughput(Throughput::Elements(n as u64));
let a64 = f64_arr(n);
group.bench_with_input(BenchmarkId::new("before_f64", n), &a64, |b, a| {
b.iter(|| black_box(before::var_f64(black_box(a), 0)))
});
group.bench_with_input(BenchmarkId::new("after_f64", n), &a64, |b, a| {
b.iter(|| black_box(after::var_f64(black_box(a), 0)))
});
}
group.finish();
}
fn bench_std(c: &mut Criterion) {
let mut group = c.benchmark_group("reduction_dispatch/std");
for &n in &SIZES {
group.throughput(Throughput::Elements(n as u64));
let a64 = f64_arr(n);
let a32 = f32_arr(n);
group.bench_with_input(BenchmarkId::new("before_f64", n), &a64, |b, a| {
b.iter(|| black_box(before::std_f64(black_box(a), 0)))
});
group.bench_with_input(BenchmarkId::new("after_f64", n), &a64, |b, a| {
b.iter(|| black_box(after::std_f64(black_box(a), 0)))
});
group.bench_with_input(BenchmarkId::new("before_f32", n), &a32, |b, a| {
b.iter(|| black_box(before::std_f32(black_box(a), 0)))
});
group.bench_with_input(BenchmarkId::new("after_f32", n), &a32, |b, a| {
b.iter(|| black_box(after::std_f32(black_box(a), 0)))
});
}
group.finish();
}
fn bench_min(c: &mut Criterion) {
let mut group = c.benchmark_group("reduction_dispatch/min");
for &n in &MINMAX_SIZES {
group.throughput(Throughput::Elements(n as u64));
let a64 = f64_arr(n);
let a32 = f32_arr(n);
bench_pair!(
group,
n,
"f64",
"f32",
a64,
a32,
before::min_f64,
after::min_f64,
before::min_f32,
after::min_f32
);
}
group.finish();
}
fn bench_max(c: &mut Criterion) {
let mut group = c.benchmark_group("reduction_dispatch/max");
for &n in &MINMAX_SIZES {
group.throughput(Throughput::Elements(n as u64));
let a64 = f64_arr(n);
let a32 = f32_arr(n);
bench_pair!(
group,
n,
"f64",
"f32",
a64,
a32,
before::max_f64,
after::max_f64,
before::max_f32,
after::max_f32
);
}
group.finish();
}
fn bench_sum_axis(c: &mut Criterion) {
let mut group = c.benchmark_group("reduction_dispatch/sum_axis1_2d");
let rows = 8usize;
for &n in &SIZES {
let cols = n / rows;
group.throughput(Throughput::Elements(n as u64));
let a64 = f64_arr(n).reshape(&[rows, cols]);
group.bench_with_input(BenchmarkId::new("before_f64", n), &a64, |b, a| {
b.iter(|| black_box(before::sum_axis1_f64(black_box(a), rows, cols)))
});
group.bench_with_input(BenchmarkId::new("after_f64", n), &a64, |b, a| {
b.iter(|| black_box(after::sum_axis1_f64(black_box(a))))
});
}
group.finish();
}
criterion_group!(
benches,
bench_sum,
bench_mean,
bench_var,
bench_var_10k_threshold,
bench_std,
bench_min,
bench_max,
bench_sum_axis
);
criterion_main!(benches);