use std::hint::black_box; use std::time::Duration; use criterion::{Criterion, criterion_main}; use libm_test::gen::random; use libm_test::gen::random::RandomInput; use libm_test::{CheckBasis, CheckCtx, GeneratorKind, MathOp, TupleCall}; /// Benchmark with this many items to get a variety const BENCH_ITER_ITEMS: usize = if cfg!(feature = "short-benchmarks") { 50 } else { 500 }; /// Extra parameters we only care about if we are benchmarking against musl. #[allow(dead_code)] struct MuslExtra { musl_fn: Option, skip_on_i586: bool, } macro_rules! musl_rand_benches { ( fn_name: $fn_name:ident, attrs: [$($attr:meta),*], fn_extra: ($skip_on_i586:expr, $musl_fn:expr), ) => { paste::paste! { $(#[$attr])* fn [< musl_bench_ $fn_name >](c: &mut Criterion) { type Op = libm_test::op::$fn_name::Routine; #[cfg(feature = "build-musl")] let musl_extra = MuslExtra::> { musl_fn: $musl_fn, skip_on_i586: $skip_on_i586, }; #[cfg(not(feature = "build-musl"))] let musl_extra = MuslExtra { musl_fn: None, skip_on_i586: $skip_on_i586, }; bench_one::(c, musl_extra); } } }; } fn bench_one(c: &mut Criterion, musl_extra: MuslExtra) where Op: MathOp, Op::RustArgs: RandomInput, { let name = Op::NAME; let ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Musl, GeneratorKind::Random); let benchvec: Vec<_> = random::get_test_cases::(&ctx).0.take(BENCH_ITER_ITEMS).collect(); // Perform a sanity check that we are benchmarking the same thing // Don't test against musl if it is not available #[cfg(feature = "build-musl")] for input in benchvec.iter().copied() { use anyhow::Context; use libm_test::CheckOutput; if cfg!(x86_no_sse) && musl_extra.skip_on_i586 { break; } let Some(musl_fn) = musl_extra.musl_fn else { continue; }; let musl_res = input.call(musl_fn); let crate_res = input.call(Op::ROUTINE); crate_res.validate(musl_res, input, &ctx).context(name).unwrap(); } #[cfg(not(feature = "build-musl"))] let _ = musl_extra; // silence unused warnings /* Option pointers are black boxed to avoid inlining in the benchmark loop */ let mut group = c.benchmark_group(name); group.bench_function("crate", |b| { b.iter(|| { let f = black_box(Op::ROUTINE); for input in benchvec.iter().copied() { input.call(f); } }) }); // Don't test against musl if it is not available #[cfg(feature = "build-musl")] { if let Some(musl_fn) = musl_extra.musl_fn { group.bench_function("musl", |b| { b.iter(|| { let f = black_box(musl_fn); for input in benchvec.iter().copied() { input.call(f); } }) }); } } } libm_macros::for_each_function! { callback: musl_rand_benches, skip: [], fn_extra: match MACRO_FN_NAME { // We pass a tuple of `(skip_on_i586, musl_fn)` // FIXME(correctness): exp functions have the wrong result on i586 exp10 | exp10f | exp2 | exp2f => (true, Some(musl_math_sys::MACRO_FN_NAME)), // Musl does not provide `f16` and `f128` functions ceilf128 | ceilf16 | copysignf128 | copysignf16 | fabsf128 | fabsf16 | fdimf128 | fdimf16 | floorf128 | floorf16 | fmaf128 | fmaxf128 | fmaxf16 | fmaximum | fmaximum_num | fmaximum_numf | fmaximum_numf128 | fmaximum_numf16 | fmaximumf | fmaximumf128 | fmaximumf16 | fminf128 | fminf16 | fminimum | fminimum_num | fminimum_numf | fminimum_numf128 | fminimum_numf16 | fminimumf | fminimumf128 | fminimumf16 | fmodf128 | fmodf16 | ldexpf128 | ldexpf16 | rintf128 | rintf16 | roundeven | roundevenf | roundevenf128 | roundevenf16 | roundf128 | roundf16 | scalbnf128 | scalbnf16 | sqrtf128 | sqrtf16 | truncf128 | truncf16 => (false, None), // By default we never skip (false) and always have a musl function available _ => (false, Some(musl_math_sys::MACRO_FN_NAME)) } } macro_rules! run_callback { ( fn_name: $fn_name:ident, attrs: [$($attr:meta),*], extra: [$criterion:ident], ) => { paste::paste! { $(#[$attr])* [< musl_bench_ $fn_name >](&mut $criterion) } }; } pub fn musl_random() { let mut criterion = Criterion::default(); // For CI, run a short 0.5s warmup and 1.0s tests. This makes benchmarks complete in // about the same time as other tests. if cfg!(feature = "short-benchmarks") { criterion = criterion .warm_up_time(Duration::from_millis(200)) .measurement_time(Duration::from_millis(600)); } criterion = criterion.configure_from_args(); libm_macros::for_each_function! { callback: run_callback, extra: [criterion], }; } criterion_main!(musl_random);