//! 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 BenchInteger for T where T: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {} // Simple PRNG so we don't have to worry about rand compatibility fn lcg(x: T) -> T where u32: AsPrimitive, 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(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(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(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);