Add benchmarks against musl libm

Add a benchmark for each function that checks against `musl_math_sys`.
This commit is contained in:
Trevor Gross 2024-10-21 18:27:01 -05:00
parent baae082fc5
commit abff7cd82a
2 changed files with 132 additions and 0 deletions

View File

@ -15,6 +15,12 @@ test-multiprecision = ["dep:az", "dep:rug"]
# Build our own musl for testing and benchmarks
build-musl = ["dep:musl-math-sys"]
# Enable report generation without bringing in more dependencies by default
benchmarking-reports = ["criterion/plotters", "criterion/html_reports"]
# Run with a reduced set of benchmarks, such as for CI
short-benchmarks = []
[dependencies]
anyhow = "1.0.90"
az = { version = "1.2.1", optional = true }
@ -32,3 +38,10 @@ getrandom = { version = "0.2", features = ["js"] }
[build-dependencies]
rand = { version = "0.8.5", optional = true }
[dev-dependencies]
criterion = { version = "0.5.1", default-features = false, features = ["cargo_bench_support"] }
[[bench]]
name = "random"
harness = false

View File

@ -0,0 +1,119 @@
use std::hint::black_box;
use std::time::Duration;
use criterion::{Criterion, criterion_main};
use libm_test::gen::random;
use libm_test::{CheckBasis, CheckCtx, TupleCall};
/// Benchmark with this many items to get a variety
const BENCH_ITER_ITEMS: usize = if cfg!(feature = "short-benchmarks") { 50 } else { 500 };
macro_rules! musl_rand_benches {
(
fn_name: $fn_name:ident,
CFn: $CFn:ty,
CArgs: $CArgs:ty,
CRet: $CRet:ty,
RustFn: $RustFn:ty,
RustArgs: $RustArgs:ty,
RustRet: $RustRet:ty,
fn_extra: $skip_on_i586:expr,
) => {
paste::paste! {
fn [< musl_bench_ $fn_name >](c: &mut Criterion) {
let fn_name = stringify!($fn_name);
let ulp = libm_test::musl_allowed_ulp(fn_name);
let ctx = CheckCtx::new(ulp, fn_name, CheckBasis::Musl);
let benchvec: Vec<_> = random::get_test_cases::<$RustArgs>(&ctx)
.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::{CheckBasis, CheckCtx, CheckOutput};
if cfg!(x86_no_sse) && $skip_on_i586 {
break;
}
let musl_res = input.call(musl_math_sys::$fn_name as $CFn);
let crate_res = input.call(libm::$fn_name as $RustFn);
let ctx = CheckCtx::new(ulp, fn_name, CheckBasis::Musl);
crate_res.validate(musl_res, input, &ctx).context(fn_name).unwrap();
}
/* Function pointers are black boxed to avoid inlining in the benchmark loop */
let mut group = c.benchmark_group(fn_name);
group.bench_function("crate", |b| b.iter(|| {
let f = black_box(libm::$fn_name as $RustFn);
for input in benchvec.iter().copied() {
input.call(f);
}
}));
// Don't test against musl if it is not available
#[cfg(feature = "build-musl")]
group.bench_function("musl", |b| b.iter(|| {
let f = black_box(musl_math_sys::$fn_name as $CFn);
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 {
// FIXME(correctness): wrong result on i586
exp10 | exp10f | exp2 | exp2f => true,
_ => false
}
}
macro_rules! run_callback {
(
fn_name: $fn_name:ident,
CFn: $_CFn:ty,
CArgs: $_CArgs:ty,
CRet: $_CRet:ty,
RustFn: $_RustFn:ty,
RustArgs: $_RustArgs:ty,
RustRet: $_RustRet:ty,
extra: [$criterion:ident],
) => {
paste::paste! {
[< 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(500))
.measurement_time(Duration::from_millis(1000));
}
criterion = criterion.configure_from_args();
libm_macros::for_each_function! {
callback: run_callback,
extra: [criterion],
};
}
criterion_main!(musl_random);