use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use numrs2::array::Array;
use numrs2::comparisons::greater;
use scirs2_core::ndarray::ArrayView1;
use scirs2_core::simd_ops::SimdUnifiedOps;
use std::hint::black_box;
const SIZES: [usize; 5] = [64, 1_000, 10_000, 100_000, 1_000_000];
fn make_f64(n: usize, scale: f64, offset: f64) -> Array<f64> {
Array::from_vec((0..n).map(|i| i as f64 * scale + offset).collect())
}
fn bench_add_mul_equal_shape(c: &mut Criterion) {
let mut group = c.benchmark_group("elementwise_dispatch/equal_shape");
for &n in &SIZES {
group.throughput(Throughput::Elements(n as u64));
let a = make_f64(n, 1.0, 0.0);
let b = make_f64(n, 0.5, 1.0);
group.bench_with_input(BenchmarkId::new("add_old_ndarray", n), &n, |bch, _| {
bch.iter(|| black_box(Array::add(&a, &b)))
});
group.bench_with_input(BenchmarkId::new("add_new_broadcast", n), &n, |bch, _| {
bch.iter(|| black_box(a.add_broadcast(&b).expect("equal shapes never fail")))
});
group.bench_with_input(BenchmarkId::new("mul_old_ndarray", n), &n, |bch, _| {
bch.iter(|| black_box(Array::multiply(&a, &b)))
});
group.bench_with_input(BenchmarkId::new("mul_new_broadcast", n), &n, |bch, _| {
bch.iter(|| black_box(a.multiply_broadcast(&b).expect("equal shapes never fail")))
});
}
group.finish();
}
fn bench_add_mul_broadcast_shape(c: &mut Criterion) {
let mut group = c.benchmark_group("elementwise_dispatch/broadcast_shape");
for &n in &SIZES {
group.throughput(Throughput::Elements(n as u64));
let a = make_f64(n, 1.0, 0.0);
let b = make_f64(1, 1.0, 3.0);
group.bench_with_input(BenchmarkId::new("add_old_ndarray", n), &n, |bch, _| {
bch.iter(|| black_box(Array::add(&a, &b)))
});
group.bench_with_input(BenchmarkId::new("add_new_broadcast", n), &n, |bch, _| {
bch.iter(|| black_box(a.add_broadcast(&b).expect("[n] and [1] always broadcast")))
});
group.bench_with_input(BenchmarkId::new("mul_old_ndarray", n), &n, |bch, _| {
bch.iter(|| black_box(Array::multiply(&a, &b)))
});
group.bench_with_input(BenchmarkId::new("mul_new_broadcast", n), &n, |bch, _| {
bch.iter(|| {
black_box(
a.multiply_broadcast(&b)
.expect("[n] and [1] always broadcast"),
)
})
});
}
group.finish();
}
fn bench_zip_loop_vs_simd_add(c: &mut Criterion) {
let mut group = c.benchmark_group("elementwise_dispatch/zip_loop_vs_simd_add");
for &n in &SIZES {
group.throughput(Throughput::Elements(n as u64));
let a = make_f64(n, 1.0, 0.0);
let b = make_f64(n, 0.5, 1.0);
let a_slice = a.as_slice().expect("from_vec is always contiguous");
let b_slice = b.as_slice().expect("from_vec is always contiguous");
group.bench_with_input(BenchmarkId::new("zip_loop", n), &n, |bch, _| {
bch.iter(|| {
black_box(
a_slice
.iter()
.zip(b_slice.iter())
.map(|(&x, &y)| x + y)
.collect::<Vec<f64>>(),
)
})
});
group.bench_with_input(BenchmarkId::new("simd_add", n), &n, |bch, _| {
bch.iter(|| {
black_box(f64::simd_add(
&ArrayView1::from(a_slice),
&ArrayView1::from(b_slice),
))
})
});
}
group.finish();
}
fn greater_old_five_copies(a: &Array<f64>, b: &Array<f64>) -> Array<bool> {
let broadcast_shape =
Array::<f64>::broadcast_shape(&a.shape(), &b.shape()).expect("bench inputs broadcast");
let a_broadcast = if a.shape() != broadcast_shape {
a.broadcast_to(&broadcast_shape)
.expect("bench inputs broadcast")
} else {
a.clone()
};
let b_broadcast = if b.shape() != broadcast_shape {
b.broadcast_to(&broadcast_shape)
.expect("bench inputs broadcast")
} else {
b.clone()
};
let a_data = a_broadcast.to_vec();
let b_data = b_broadcast.to_vec();
let result: Vec<bool> = a_data
.iter()
.zip(b_data.iter())
.map(|(x, y)| x > y)
.collect();
Array::from_vec(result).reshape(&broadcast_shape)
}
fn bench_greater(c: &mut Criterion) {
let mut group = c.benchmark_group("elementwise_dispatch/comparisons_greater");
let n = 100_000usize;
group.throughput(Throughput::Elements(n as u64));
let a = make_f64(n, 1.0, 0.0);
let b = make_f64(n, 1.0, -1.0);
group.bench_function(BenchmarkId::new("greater_old_5_copies", n), |bch| {
bch.iter(|| black_box(greater_old_five_copies(&a, &b)))
});
group.bench_function(BenchmarkId::new("greater_new_cow_operand", n), |bch| {
bch.iter(|| black_box(greater(&a, &b).expect("equal shapes never fail")))
});
group.finish();
}
criterion_group!(
benches,
bench_add_mul_equal_shape,
bench_add_mul_broadcast_shape,
bench_zip_loop_vs_simd_add,
bench_greater,
);
criterion_main!(benches);