123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- //! Benchmark sqrt and cbrt
- #![feature(test)]
- extern crate num_integer;
- extern crate num_traits;
- extern crate test;
- use num_integer::Integer;
- use num_traits::{AsPrimitive, PrimInt, WrappingAdd, WrappingMul};
- use std::cmp::{max, min};
- use std::fmt::Debug;
- use test::{black_box, Bencher};
- // --- Utilities for RNG ----------------------------------------------------
- trait BenchInteger: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {}
- impl<T> BenchInteger for T where T: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {}
- // Simple PRNG so we don't have to worry about rand compatibility
- fn lcg<T>(x: T) -> T
- where
- u32: AsPrimitive<T>,
- T: BenchInteger,
- {
- // LCG parameters from Numerical Recipes
- // (but we're applying it to arbitrary sizes)
- const LCG_A: u32 = 1664525;
- const LCG_C: u32 = 1013904223;
- x.wrapping_mul(&LCG_A.as_()).wrapping_add(&LCG_C.as_())
- }
- // --- Alt. Implementations -------------------------------------------------
- trait NaiveAverage {
- fn naive_average_ceil(&self, other: &Self) -> Self;
- fn naive_average_floor(&self, other: &Self) -> Self;
- }
- trait UncheckedAverage {
- fn unchecked_average_ceil(&self, other: &Self) -> Self;
- fn unchecked_average_floor(&self, other: &Self) -> Self;
- }
- trait ModuloAverage {
- fn modulo_average_ceil(&self, other: &Self) -> Self;
- fn modulo_average_floor(&self, other: &Self) -> Self;
- }
- macro_rules! naive_average {
- ($T:ident) => {
- impl super::NaiveAverage for $T {
- fn naive_average_floor(&self, other: &$T) -> $T {
- match self.checked_add(*other) {
- Some(z) => Integer::div_floor(&z, &2),
- None => {
- if self > other {
- let diff = self - other;
- other + Integer::div_floor(&diff, &2)
- } else {
- let diff = other - self;
- self + Integer::div_floor(&diff, &2)
- }
- }
- }
- }
- fn naive_average_ceil(&self, other: &$T) -> $T {
- match self.checked_add(*other) {
- Some(z) => Integer::div_ceil(&z, &2),
- None => {
- if self > other {
- let diff = self - other;
- self - Integer::div_floor(&diff, &2)
- } else {
- let diff = other - self;
- other - Integer::div_floor(&diff, &2)
- }
- }
- }
- }
- }
- };
- }
- macro_rules! unchecked_average {
- ($T:ident) => {
- impl super::UncheckedAverage for $T {
- fn unchecked_average_floor(&self, other: &$T) -> $T {
- self.wrapping_add(*other) / 2
- }
- fn unchecked_average_ceil(&self, other: &$T) -> $T {
- (self.wrapping_add(*other) / 2).wrapping_add(1)
- }
- }
- };
- }
- macro_rules! modulo_average {
- ($T:ident) => {
- impl super::ModuloAverage for $T {
- fn modulo_average_ceil(&self, other: &$T) -> $T {
- let (q1, r1) = self.div_mod_floor(&2);
- let (q2, r2) = other.div_mod_floor(&2);
- q1 + q2 + (r1 | r2)
- }
- fn modulo_average_floor(&self, other: &$T) -> $T {
- let (q1, r1) = self.div_mod_floor(&2);
- let (q2, r2) = other.div_mod_floor(&2);
- q1 + q2 + (r1 * r2)
- }
- }
- };
- }
- // --- Bench functions ------------------------------------------------------
- fn bench_unchecked<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
- where
- T: Integer + Debug + Copy,
- F: Fn(&T, &T) -> T,
- {
- b.iter(|| {
- for (x, y) in v {
- black_box(f(x, y));
- }
- });
- }
- fn bench_ceil<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
- where
- T: Integer + Debug + Copy,
- F: Fn(&T, &T) -> T,
- {
- for &(i, j) in v {
- let rt = f(&i, &j);
- let (a, b) = (min(i, j), max(i, j));
- // if both number are the same sign, check rt is in the middle
- if (a < T::zero()) == (b < T::zero()) {
- if (b - a).is_even() {
- assert_eq!(rt - a, b - rt);
- } else {
- assert_eq!(rt - a, b - rt + T::one());
- }
- // if both number have a different sign,
- } else {
- if (a + b).is_even() {
- assert_eq!(rt, (a + b) / (T::one() + T::one()))
- } else {
- assert_eq!(rt, (a + b + T::one()) / (T::one() + T::one()))
- }
- }
- }
- bench_unchecked(b, v, f);
- }
- fn bench_floor<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
- where
- T: Integer + Debug + Copy,
- F: Fn(&T, &T) -> T,
- {
- for &(i, j) in v {
- let rt = f(&i, &j);
- let (a, b) = (min(i, j), max(i, j));
- // if both number are the same sign, check rt is in the middle
- if (a < T::zero()) == (b < T::zero()) {
- if (b - a).is_even() {
- assert_eq!(rt - a, b - rt);
- } else {
- assert_eq!(rt - a + T::one(), b - rt);
- }
- // if both number have a different sign,
- } else {
- if (a + b).is_even() {
- assert_eq!(rt, (a + b) / (T::one() + T::one()))
- } else {
- assert_eq!(rt, (a + b - T::one()) / (T::one() + T::one()))
- }
- }
- }
- bench_unchecked(b, v, f);
- }
- // --- Bench implementation -------------------------------------------------
- macro_rules! bench_average {
- ($($T:ident),*) => {$(
- mod $T {
- use test::Bencher;
- use num_integer::{Average, Integer};
- use super::{UncheckedAverage, NaiveAverage, ModuloAverage};
- use super::{bench_ceil, bench_floor, bench_unchecked};
- naive_average!($T);
- unchecked_average!($T);
- modulo_average!($T);
- const SIZE: $T = 30;
- fn overflowing() -> Vec<($T, $T)> {
- (($T::max_value()-SIZE)..$T::max_value())
- .flat_map(|x| -> Vec<_> {
- (($T::max_value()-100)..($T::max_value()-100+SIZE))
- .map(|y| (x, y))
- .collect()
- })
- .collect()
- }
- fn small() -> Vec<($T, $T)> {
- (0..SIZE)
- .flat_map(|x| -> Vec<_> {(0..SIZE).map(|y| (x, y)).collect()})
- .collect()
- }
- fn rand() -> Vec<($T, $T)> {
- small()
- .into_iter()
- .map(|(x, y)| (super::lcg(x), super::lcg(y)))
- .collect()
- }
- mod ceil {
- use super::*;
- mod small {
- use super::*;
- #[bench]
- fn optimized(b: &mut Bencher) {
- let v = small();
- bench_ceil(b, &v, |x: &$T, y: &$T| x.average_ceil(y));
- }
- #[bench]
- fn naive(b: &mut Bencher) {
- let v = small();
- bench_ceil(b, &v, |x: &$T, y: &$T| x.naive_average_ceil(y));
- }
- #[bench]
- fn unchecked(b: &mut Bencher) {
- let v = small();
- bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_ceil(y));
- }
- #[bench]
- fn modulo(b: &mut Bencher) {
- let v = small();
- bench_ceil(b, &v, |x: &$T, y: &$T| x.modulo_average_ceil(y));
- }
- }
- mod overflowing {
- use super::*;
- #[bench]
- fn optimized(b: &mut Bencher) {
- let v = overflowing();
- bench_ceil(b, &v, |x: &$T, y: &$T| x.average_ceil(y));
- }
- #[bench]
- fn naive(b: &mut Bencher) {
- let v = overflowing();
- bench_ceil(b, &v, |x: &$T, y: &$T| x.naive_average_ceil(y));
- }
- #[bench]
- fn unchecked(b: &mut Bencher) {
- let v = overflowing();
- bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_ceil(y));
- }
- #[bench]
- fn modulo(b: &mut Bencher) {
- let v = overflowing();
- bench_ceil(b, &v, |x: &$T, y: &$T| x.modulo_average_ceil(y));
- }
- }
- mod rand {
- use super::*;
- #[bench]
- fn optimized(b: &mut Bencher) {
- let v = rand();
- bench_ceil(b, &v, |x: &$T, y: &$T| x.average_ceil(y));
- }
- #[bench]
- fn naive(b: &mut Bencher) {
- let v = rand();
- bench_ceil(b, &v, |x: &$T, y: &$T| x.naive_average_ceil(y));
- }
- #[bench]
- fn unchecked(b: &mut Bencher) {
- let v = rand();
- bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_ceil(y));
- }
- #[bench]
- fn modulo(b: &mut Bencher) {
- let v = rand();
- bench_ceil(b, &v, |x: &$T, y: &$T| x.modulo_average_ceil(y));
- }
- }
- }
- mod floor {
- use super::*;
- mod small {
- use super::*;
- #[bench]
- fn optimized(b: &mut Bencher) {
- let v = small();
- bench_floor(b, &v, |x: &$T, y: &$T| x.average_floor(y));
- }
- #[bench]
- fn naive(b: &mut Bencher) {
- let v = small();
- bench_floor(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
- }
- #[bench]
- fn unchecked(b: &mut Bencher) {
- let v = small();
- bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
- }
- #[bench]
- fn modulo(b: &mut Bencher) {
- let v = small();
- bench_floor(b, &v, |x: &$T, y: &$T| x.modulo_average_floor(y));
- }
- }
- mod overflowing {
- use super::*;
- #[bench]
- fn optimized(b: &mut Bencher) {
- let v = overflowing();
- bench_floor(b, &v, |x: &$T, y: &$T| x.average_floor(y));
- }
- #[bench]
- fn naive(b: &mut Bencher) {
- let v = overflowing();
- bench_floor(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
- }
- #[bench]
- fn unchecked(b: &mut Bencher) {
- let v = overflowing();
- bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
- }
- #[bench]
- fn modulo(b: &mut Bencher) {
- let v = overflowing();
- bench_floor(b, &v, |x: &$T, y: &$T| x.modulo_average_floor(y));
- }
- }
- mod rand {
- use super::*;
- #[bench]
- fn optimized(b: &mut Bencher) {
- let v = rand();
- bench_floor(b, &v, |x: &$T, y: &$T| x.average_floor(y));
- }
- #[bench]
- fn naive(b: &mut Bencher) {
- let v = rand();
- bench_floor(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
- }
- #[bench]
- fn unchecked(b: &mut Bencher) {
- let v = rand();
- bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
- }
- #[bench]
- fn modulo(b: &mut Bencher) {
- let v = rand();
- bench_floor(b, &v, |x: &$T, y: &$T| x.modulo_average_floor(y));
- }
- }
- }
- }
- )*}
- }
- bench_average!(i8, i16, i32, i64, i128, isize);
- bench_average!(u8, u16, u32, u64, u128, usize);
|