use rustnum as np;
use std::hint::black_box;
use std::time::Instant;
fn measure<F: np::linalg::LinalgFloat>(n: usize, threads: usize, repeats: usize) {
let a = np::Array::from_shape_fn([n, n], |c| {
F::from_f64(((c[0] * 17 + c[1] * 7) % 101) as f64 / 101.0 - 0.5)
});
black_box(a.matmul(&a));
let mut samples = Vec::new();
for _ in 0..if n >= 4096 { 2 } else { 6 } {
let start = Instant::now();
for _ in 0..repeats {
black_box(a.matmul(&a));
}
samples.push(start.elapsed().as_secs_f64() / repeats as f64);
}
samples.sort_by(f64::total_cmp);
println!(
"{} n={n} threads={threads} ms={:.3} GF/s={:.2}",
F::DTYPE,
samples[0] * 1000.0,
2.0 * (n as f64).powi(3) / samples[0] / 1e9
);
}
fn sweep(threads: usize, small: bool) {
let sizes: &[usize] = if small {
&[64, 96, 128, 192, 256, 384, 512]
} else {
&[1024, 2048, 4096]
};
let one: Option<usize> = std::env::args().find_map(|a| a.strip_prefix("--n=")?.parse().ok());
let single = [one.unwrap_or(0)];
for &n in if one.is_some() { &single[..] } else { sizes } {
let repeats = if small {
(16_777_216 / (n * n * n)).max(3)
} else {
1
};
measure::<f64>(n, threads, repeats);
measure::<f32>(n, threads, repeats);
}
}
fn main() {
let small = std::env::args().any(|arg| arg == "--small");
let only: Option<usize> =
std::env::args().find_map(|a| a.strip_prefix("--threads=")?.parse().ok());
#[cfg(feature = "rayon")]
for threads in [
1,
std::thread::available_parallelism().map_or(1, usize::from),
] {
if only.is_some_and(|t| t != threads) {
continue;
}
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(threads)
.build()
.unwrap();
pool.install(|| sweep(threads, small));
}
#[cfg(not(feature = "rayon"))]
{
let _ = only;
sweep(1, small);
}
}