|
| 1 | +use rand::Rng; |
| 2 | +use test::{black_box, Bencher}; |
| 3 | + |
| 4 | +const ITERATIONS: usize = 128; // Uses an ITERATIONS * 20 Byte stack allocation |
| 5 | +type IntType = i128; // Hardest native type to multiply |
| 6 | +const EXPONENT_MAX: u32 = 31; |
| 7 | +const MAX_BASE: IntType = 17; // +-17 ** 31 <= IntType::MAX |
| 8 | + |
| 9 | +macro_rules! pow_bench_template { |
| 10 | + ($name:ident, $inner_macro:ident, $base_macro:ident) => { |
| 11 | + #[bench] |
| 12 | + fn $name(bench: &mut Bencher) { |
| 13 | + // Frequent black_box calls can add latency and prevent optimizations, so for |
| 14 | + // variable parameters we premake an array and pass the |
| 15 | + // reference through black_box outside of the loop. |
| 16 | + let mut rng = crate::bench_rng(); |
| 17 | + let base_array: [IntType; ITERATIONS] = |
| 18 | + core::array::from_fn(|_| rng.gen_range((-MAX_BASE..=MAX_BASE))); |
| 19 | + let exp_array: [u32; ITERATIONS] = |
| 20 | + core::array::from_fn(|_| rng.gen_range((0..=EXPONENT_MAX))); |
| 21 | + |
| 22 | + bench.iter(|| { |
| 23 | + #[allow(unused, unused_mut)] |
| 24 | + let mut base_iter = black_box(&base_array).into_iter(); |
| 25 | + let mut exp_iter = black_box(&exp_array).into_iter(); |
| 26 | + |
| 27 | + (0..ITERATIONS).fold((0 as IntType, false), |acc, _| { |
| 28 | + // Sometimes constants don't propogate all the way to the |
| 29 | + // inside of the loop, so we call a custom expression every cycle |
| 30 | + // rather than iter::repeat(CONST) |
| 31 | + let base: IntType = $base_macro!(base_iter); |
| 32 | + let exp: u32 = *exp_iter.next().unwrap(); |
| 33 | + |
| 34 | + let r: (IntType, bool) = $inner_macro!(base, exp); |
| 35 | + (acc.0 ^ r.0, acc.1 ^ r.1) |
| 36 | + }) |
| 37 | + }); |
| 38 | + } |
| 39 | + }; |
| 40 | +} |
| 41 | + |
| 42 | +// This may panic if it overflows. |
| 43 | +macro_rules! inner_pow { |
| 44 | + ($base:ident, $exp:ident) => { |
| 45 | + ($base.pow($exp), false) |
| 46 | + }; |
| 47 | +} |
| 48 | + |
| 49 | +macro_rules! inner_wrapping { |
| 50 | + ($base:ident, $exp:ident) => { |
| 51 | + ($base.wrapping_pow($exp), false) |
| 52 | + }; |
| 53 | +} |
| 54 | + |
| 55 | +macro_rules! inner_overflowing { |
| 56 | + ($base:ident, $exp:ident) => { |
| 57 | + $base.overflowing_pow($exp) |
| 58 | + }; |
| 59 | +} |
| 60 | + |
| 61 | +// This will panic if it overflows. |
| 62 | +macro_rules! inner_checked_unwrapped { |
| 63 | + ($base:ident, $exp:ident) => { |
| 64 | + ($base.checked_pow($exp).unwrap(), false) |
| 65 | + }; |
| 66 | +} |
| 67 | + |
| 68 | +macro_rules! inner_saturating { |
| 69 | + ($base:ident, $exp:ident) => { |
| 70 | + ($base.saturating_pow($exp), false) |
| 71 | + }; |
| 72 | +} |
| 73 | + |
| 74 | +macro_rules! make_const_base { |
| 75 | + ($name:ident, $x:literal) => { |
| 76 | + macro_rules! $name { |
| 77 | + ($iter:ident) => { |
| 78 | + $x |
| 79 | + }; |
| 80 | + } |
| 81 | + }; |
| 82 | +} |
| 83 | + |
| 84 | +make_const_base!(const_base_m7, -7); |
| 85 | +make_const_base!(const_base_m8, -8); |
| 86 | + |
| 87 | +macro_rules! variable_base { |
| 88 | + ($iter:ident) => { |
| 89 | + *$iter.next().unwrap() |
| 90 | + }; |
| 91 | +} |
| 92 | + |
| 93 | +pow_bench_template!(pow_variable, inner_pow, variable_base); |
| 94 | +pow_bench_template!(wrapping_pow_variable, inner_wrapping, variable_base); |
| 95 | +pow_bench_template!(overflowing_pow_variable, inner_overflowing, variable_base); |
| 96 | +pow_bench_template!(checked_pow_variable, inner_checked_unwrapped, variable_base); |
| 97 | +pow_bench_template!(saturating_pow_variable, inner_saturating, variable_base); |
| 98 | +pow_bench_template!(pow_m7, inner_pow, const_base_m7); |
| 99 | +pow_bench_template!(pow_m8, inner_pow, const_base_m8); |
0 commit comments