| use rand::Rng; |
| use test::{black_box, Bencher}; |
| |
| const ITERATIONS: usize = 128; // Uses an ITERATIONS * 20 Byte stack allocation |
| type IntType = i128; // Hardest native type to multiply |
| const EXPONENT_MAX: u32 = 31; |
| const MAX_BASE: IntType = 17; // +-17 ** 31 <= IntType::MAX |
| |
| macro_rules! pow_bench_template { |
| ($name:ident, $inner_macro:ident, $base_macro:ident) => { |
| #[bench] |
| fn $name(bench: &mut Bencher) { |
| // Frequent black_box calls can add latency and prevent optimizations, so for |
| // variable parameters we premake an array and pass the |
| // reference through black_box outside of the loop. |
| let mut rng = crate::bench_rng(); |
| let base_array: [IntType; ITERATIONS] = |
| core::array::from_fn(|_| rng.gen_range((-MAX_BASE..=MAX_BASE))); |
| let exp_array: [u32; ITERATIONS] = |
| core::array::from_fn(|_| rng.gen_range((0..=EXPONENT_MAX))); |
| |
| bench.iter(|| { |
| #[allow(unused, unused_mut)] |
| let mut base_iter = black_box(&base_array).into_iter(); |
| let mut exp_iter = black_box(&exp_array).into_iter(); |
| |
| (0..ITERATIONS).fold((0 as IntType, false), |acc, _| { |
| // Sometimes constants don't propogate all the way to the |
| // inside of the loop, so we call a custom expression every cycle |
| // rather than iter::repeat(CONST) |
| let base: IntType = $base_macro!(base_iter); |
| let exp: u32 = *exp_iter.next().unwrap(); |
| |
| let r: (IntType, bool) = $inner_macro!(base, exp); |
| (acc.0 ^ r.0, acc.1 ^ r.1) |
| }) |
| }); |
| } |
| }; |
| } |
| |
| // This may panic if it overflows. |
| macro_rules! inner_pow { |
| ($base:ident, $exp:ident) => { |
| ($base.pow($exp), false) |
| }; |
| } |
| |
| macro_rules! inner_wrapping { |
| ($base:ident, $exp:ident) => { |
| ($base.wrapping_pow($exp), false) |
| }; |
| } |
| |
| macro_rules! inner_overflowing { |
| ($base:ident, $exp:ident) => { |
| $base.overflowing_pow($exp) |
| }; |
| } |
| |
| // This will panic if it overflows. |
| macro_rules! inner_checked_unwrapped { |
| ($base:ident, $exp:ident) => { |
| ($base.checked_pow($exp).unwrap(), false) |
| }; |
| } |
| |
| macro_rules! inner_saturating { |
| ($base:ident, $exp:ident) => { |
| ($base.saturating_pow($exp), false) |
| }; |
| } |
| |
| macro_rules! make_const_base { |
| ($name:ident, $x:literal) => { |
| macro_rules! $name { |
| ($iter:ident) => { |
| $x |
| }; |
| } |
| }; |
| } |
| |
| make_const_base!(const_base_m7, -7); |
| make_const_base!(const_base_m8, -8); |
| |
| macro_rules! variable_base { |
| ($iter:ident) => { |
| *$iter.next().unwrap() |
| }; |
| } |
| |
| pow_bench_template!(pow_variable, inner_pow, variable_base); |
| pow_bench_template!(wrapping_pow_variable, inner_wrapping, variable_base); |
| pow_bench_template!(overflowing_pow_variable, inner_overflowing, variable_base); |
| pow_bench_template!(checked_pow_variable, inner_checked_unwrapped, variable_base); |
| pow_bench_template!(saturating_pow_variable, inner_saturating, variable_base); |
| pow_bench_template!(pow_m7, inner_pow, const_base_m7); |
| pow_bench_template!(pow_m8, inner_pow, const_base_m8); |