diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 0adbab92..d3705883 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -23,9 +23,9 @@ jobs: with: tool: cargo-codspeed - name: Build the benchmark target(s) - run: cargo codspeed build --profile profiling -p ruint bench_uint + run: cargo codspeed build --profile profiling -p ruint-bench bench - name: Run the benchmarks uses: CodSpeedHQ/action@v3 with: - run: cargo codspeed run -p ruint bench_uint + run: cargo codspeed run -p ruint-bench bench token: ${{ secrets.CODSPEED_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index be35f7ad..e2a226f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true +[lib] +bench = false + [package.metadata.docs.rs] all-features = true rustdoc-args = [ @@ -24,7 +27,7 @@ rustdoc-args = [ ] [workspace] -members = [".", "ruint-macro"] +members = [".", "ruint-macro", "ruint-bench"] resolver = "2" [workspace.package] @@ -35,12 +38,6 @@ license = "MIT" homepage = "https://github.com/recmo/uint" repository = "https://github.com/recmo/uint" -[[bench]] -name = "bench_uint" -harness = false -path = "benches/bench.rs" -required-features = ["std"] - [dependencies] ruint-macro = { version = "1.2.1", path = "ruint-macro" } diff --git a/benches/benches/algorithms/div/reciprocal.rs b/benches/benches/algorithms/div/reciprocal.rs index 7e70bebb..2249b7a5 100644 --- a/benches/benches/algorithms/div/reciprocal.rs +++ b/benches/benches/algorithms/div/reciprocal.rs @@ -8,34 +8,28 @@ pub fn group(criterion: &mut Criterion) { } fn bench_reciprocal_ref(criterion: &mut Criterion) { - let mut rng = rng(); - criterion.bench_function("algo/div/reciprocal/ref", move |bencher| { - bencher.iter_batched( - || rng.random::() | (1 << 63), - |a| black_box(reciprocal_ref(black_box(a))), - BatchSize::SmallInput, - ); - }); + bench_arbitrary_with( + criterion, + "algo/div/reciprocal/ref", + u64::arbitrary().prop_map(|a| a | (1 << 63)), + reciprocal_ref, + ); } fn bench_reciprocal_mg10(criterion: &mut Criterion) { - let mut rng = rng(); - criterion.bench_function("algo/div/reciprocal/mg10", move |bencher| { - bencher.iter_batched( - || rng.random::() | (1 << 63), - |a| black_box(reciprocal_mg10(black_box(a))), - BatchSize::SmallInput, - ); - }); + bench_arbitrary_with( + criterion, + "algo/div/reciprocal/mg10", + u64::arbitrary().prop_map(|a| a | (1 << 63)), + reciprocal_mg10, + ); } fn bench_reciprocal_2_mg10(criterion: &mut Criterion) { - let mut rng = rng(); - criterion.bench_function("algo/div/reciprocal_2/mg10", move |bencher| { - bencher.iter_batched( - || rng.random::() | (1 << 127), - |a| black_box(reciprocal_2_mg10(black_box(a))), - BatchSize::SmallInput, - ); - }); + bench_arbitrary_with( + criterion, + "algo/div/reciprocal_2/mg10", + u128::arbitrary().prop_map(|a| a | (1u128 << 127)), + reciprocal_2_mg10, + ); } diff --git a/benches/benches/algorithms/div/small.rs b/benches/benches/algorithms/div/small.rs index b0323294..b75c30ff 100644 --- a/benches/benches/algorithms/div/small.rs +++ b/benches/benches/algorithms/div/small.rs @@ -8,96 +8,63 @@ pub fn group(criterion: &mut Criterion) { bench_div_3x2_mg10(criterion); } +fn input_2x1() -> impl Strategy { + (u64::arbitrary(), u64::arbitrary(), u64::arbitrary()).prop_map(|(q, r, d)| { + let d = d | (1 << 63); + let r = r % d; + let n = u128::from(q) * u128::from(d) + u128::from(r); + (n, d) + }) +} + +fn input_3x2() -> impl Strategy { + (u64::arbitrary(), u128::arbitrary(), u128::arbitrary()).prop_map(|(q, r, d)| { + let d = d | (1 << 127); + let r = r % d; + let (n21, n0) = { + let d1 = (d >> 64) as u64; + let d0 = d as u64; + let r1 = (r >> 64) as u64; + let r0 = r as u64; + // n = q * d + r + let n10 = u128::from(q) * u128::from(d0) + u128::from(r0); + let n0 = n10 as u64; + let n21 = (n10 >> 64) + u128::from(q) * u128::from(d1) + u128::from(r1); + (n21, n0) + }; + (n21, n0, d) + }) +} + fn bench_div_2x1_ref(criterion: &mut Criterion) { - let mut rng = rng(); - criterion.bench_function("algo/div/2x1/ref", move |bencher| { - bencher.iter_batched( - || { - let q: u64 = rng.random(); - let r: u64 = rng.random(); - let d = rng.random::() | (1 << 63); - let r = r % d; - let n = u128::from(q) * u128::from(d) + u128::from(r); - (n, d) - }, - |(u, d)| black_box(div_2x1_ref(u, d)), - BatchSize::SmallInput, - ); + bench_arbitrary_with(criterion, "algo/div/2x1/ref", input_2x1(), |(n, d)| { + div_2x1_ref(n, d) }); } fn bench_div_2x1_mg10(criterion: &mut Criterion) { - let mut rng = rng(); - criterion.bench_function("algo/div/2x1/mg10", move |bencher| { - bencher.iter_batched( - || { - let q: u64 = rng.random(); - let r: u64 = rng.random(); - let d = rng.random::() | (1 << 63); - let r = r % d; - let n = u128::from(q) * u128::from(d) + u128::from(r); - let v = reciprocal(d); - (n, d, v) - }, - |(u, d, v)| black_box(div_2x1_mg10(u, d, v)), - BatchSize::SmallInput, - ); - }); + bench_arbitrary_with( + criterion, + "algo/div/2x1/mg10", + input_2x1().prop_map(|(n, d)| (n, d, reciprocal(d))), + |(n, d, v)| div_2x1_mg10(n, d, v), + ); } fn bench_div_3x2_ref(criterion: &mut Criterion) { - let mut rng = rng(); - criterion.bench_function("algo/div/3x2/ref", move |bencher| { - bencher.iter_batched( - || { - let q: u64 = rng.random(); - let r: u128 = rng.random(); - let d = rng.random::() | (1 << 127); - let r = r % d; - let (n21, n0) = { - let d1 = (d >> 64) as u64; - let d0 = d as u64; - let r1 = (r >> 64) as u64; - let r0 = r as u64; - // n = q * d + r - let n10 = u128::from(q) * u128::from(d0) + u128::from(r0); - let n0 = n10 as u64; - let n21 = (n10 >> 64) + u128::from(q) * u128::from(d1) + u128::from(r1); - (n21, n0) - }; - (n21, n0, d) - }, - |(n21, n0, d)| black_box(div_3x2_ref(n21, n0, d)), - BatchSize::SmallInput, - ); - }); + bench_arbitrary_with( + criterion, + "algo/div/3x2/ref", + input_3x2(), + |(n21, n0, d)| div_3x2_ref(n21, n0, d), + ); } fn bench_div_3x2_mg10(criterion: &mut Criterion) { - let mut rng = rng(); - criterion.bench_function("algo/div/3x2/mg10", move |bencher| { - bencher.iter_batched( - || { - let q: u64 = rng.random(); - let r: u128 = rng.random(); - let d = rng.random::() | (1 << 127); - let r = r % d; - let (n21, n0) = { - let d1 = (d >> 64) as u64; - let d0 = d as u64; - let r1 = (r >> 64) as u64; - let r0 = r as u64; - // n = q * d + r - let n10 = u128::from(q) * u128::from(d0) + u128::from(r0); - let n0 = n10 as u64; - let n21 = (n10 >> 64) + u128::from(q) * u128::from(d1) + u128::from(r1); - (n21, n0) - }; - let v = reciprocal_2(d); - (n21, n0, d, v) - }, - |(n21, n0, d, v)| black_box(div_3x2_mg10(n21, n0, d, v)), - BatchSize::SmallInput, - ); - }); + bench_arbitrary_with( + criterion, + "algo/div/3x2/mg10", + input_3x2().prop_map(|(n21, n0, d)| (n21, n0, d, reciprocal_2(d))), + |(n21, n0, d, v)| div_3x2_mg10(n21, n0, d, v), + ); } diff --git a/benches/benches/algorithms/gcd.rs b/benches/benches/algorithms/gcd.rs index 19004dd3..330f08f2 100644 --- a/benches/benches/algorithms/gcd.rs +++ b/benches/benches/algorithms/gcd.rs @@ -1,5 +1,4 @@ use crate::prelude::*; -use core::cmp::{max, min}; use ruint::algorithms::LehmerMatrix as Matrix; pub fn group(criterion: &mut Criterion) { @@ -12,54 +11,32 @@ pub fn group(criterion: &mut Criterion) { } fn bench_from_u64(criterion: &mut Criterion) { - let input = (u64::arbitrary(), u64::arbitrary()); - let mut runner = TestRunner::deterministic(); - criterion.bench_function("algorithms/gcd/matrix/from_u64", move |bencher| { - bencher.iter_batched( - || { - let (a, b) = input.new_tree(&mut runner).unwrap().current(); - (max(a, b), min(a, b)) - }, - |(a, b)| black_box(Matrix::from_u64(black_box(a), black_box(b))), - BatchSize::SmallInput, - ); - }); + bench_arbitrary_with( + criterion, + "algorithms/gcd/matrix/from_u64", + input::(), + |(a, b)| Matrix::from_u64(a, b), + ); } fn bench_from_u64_prefix(criterion: &mut Criterion) { - let input = (u64::arbitrary(), u64::arbitrary()); - let mut runner = TestRunner::deterministic(); - criterion.bench_function("algorithms/gcd/matrix/from_u64_prefix", move |bencher| { - bencher.iter_batched( - || { - let (a, b) = input.new_tree(&mut runner).unwrap().current(); - (max(a, b), min(a, b)) - }, - |(a, b)| black_box(Matrix::from_u64_prefix(black_box(a), black_box(b))), - BatchSize::SmallInput, - ); - }); + bench_arbitrary_with( + criterion, + "algorithms/gcd/matrix/from_u64_prefix", + input::(), + |(a, b)| Matrix::from_u64_prefix(a, b), + ); } fn bench_apply(criterion: &mut Criterion) { - let input = ( - Uint::::arbitrary(), - Uint::::arbitrary(), - ); - let mut runner = TestRunner::deterministic(); - criterion.bench_function( + bench_arbitrary_with( + criterion, &format!("algorithms/gcd/matrix/apply/{BITS}"), - move |bencher| { - bencher.iter_batched( - || { - let (a, b) = input.new_tree(&mut runner).unwrap().current(); - let (a, b) = (max(a, b), min(a, b)); - let m = Matrix::from(a, b); - (a, b, m) - }, - |(a, b, m)| black_box(m).apply(&mut black_box(a), &mut black_box(b)), - BatchSize::SmallInput, - ); - }, + input::>().prop_map(|(a, b)| (Matrix::from(a, b), a, b)), + |(m, mut a, mut b)| m.apply(&mut a, &mut b), ); } + +fn input() -> impl Strategy { + <(T, T)>::arbitrary().prop_map(|(a, b)| if a >= b { (a, b) } else { (b, a) }) +} diff --git a/benches/benches/algorithms/mul.rs b/benches/benches/algorithms/mul.rs index 6fd8f58d..a6149e51 100644 --- a/benches/benches/algorithms/mul.rs +++ b/benches/benches/algorithms/mul.rs @@ -6,21 +6,14 @@ pub fn group(criterion: &mut Criterion) { } fn bench_addmul_nnn(criterion: &mut Criterion) { - const_for!(SIZE in [0,1,2,3,4,5,6] { - let mut rng = rng(); - criterion.bench_function(&format!("algo/addmul_n/{SIZE}"), move |bencher| { - bencher.iter_batched( - || ( - rng.random::<[u64; SIZE]>(), - rng.random::<[u64; SIZE]>(), - rng.random::<[u64; SIZE]>(), - ), - |(mut lhs, a, b)| { - addmul_n(&mut lhs, &a, &b); - black_box(lhs) - }, - BatchSize::SmallInput, - ); - }); + const_for!(SIZE in [1,2,3,4,5,6] { + bench_arbitrary::<([u64; SIZE], [u64; SIZE], [u64; SIZE]), _>( + criterion, + &format!("algo/addmul_n/{SIZE}"), + |(mut lhs, a, b)| { + addmul_n(&mut lhs, &a, &b); + lhs + }, + ); }); } diff --git a/benches/benches/prelude.rs b/benches/benches/prelude.rs index a4429d9c..6cb89677 100644 --- a/benches/benches/prelude.rs +++ b/benches/benches/prelude.rs @@ -7,7 +7,6 @@ pub use proptest::{ strategy::{Strategy, ValueTree}, test_runner::TestRunner, }; -pub use rand_09::{self as rand, prelude::*, rng}; pub use ruint::{const_for, nlimbs, uint, Bits, Uint, UintTryFrom, UintTryTo}; pub use std::hint::black_box; @@ -55,11 +54,10 @@ pub fn bench_arbitrary_with( criterion: &mut criterion::Criterion, name: &str, input: T, - f: impl FnMut(T::Value) -> U, + mut f: impl FnMut(T::Value) -> U, ) { let mut runner = TestRunner::deterministic(); let mut setup = mk_setup(&input, &mut runner); - let mut f = black_box_routine(f); criterion.bench_function(name, move |bencher| { bencher.iter_batched(&mut setup, &mut f, BatchSize::SmallInput); }); @@ -71,7 +69,3 @@ fn mk_setup<'a, T: Strategy>( ) -> impl FnMut() -> T::Value + 'a { move || input.new_tree(runner).unwrap().current() } - -fn black_box_routine(mut f: impl FnMut(T) -> U) -> impl FnMut(T) -> U { - move |input| black_box(f(black_box(input))) -} diff --git a/ruint-bench/Cargo.toml b/ruint-bench/Cargo.toml new file mode 100644 index 00000000..7499cd17 --- /dev/null +++ b/ruint-bench/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ruint-bench" +description = "ruint benchmarks" +version = "0.0.0" +publish = false + +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lib] +test = false +doctest = false +doc = false +harness = false + +[dev-dependencies] +ruint = { path = "..", features = ["std", "proptest"] } + +criterion = { version = "2.10", package = "codspeed-criterion-compat" } +proptest = "1" + +[[bench]] +name = "bench" +harness = false +path = "../benches/bench.rs" diff --git a/ruint-bench/src/lib.rs b/ruint-bench/src/lib.rs new file mode 100644 index 00000000..94ce55c5 --- /dev/null +++ b/ruint-bench/src/lib.rs @@ -0,0 +1,7 @@ +//! Benchmarks for the `ruint` crate. +//! +//! This is a separate crate to avoid building unnecessary dev-dependencies and +//! the test suite when running these benchmarks in CI. + +#[cfg(test)] +fn main() {}