average.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. //! Benchmark sqrt and cbrt
  2. #![feature(test)]
  3. extern crate num_integer;
  4. extern crate num_traits;
  5. extern crate test;
  6. use num_integer::Integer;
  7. use num_traits::{AsPrimitive, PrimInt, WrappingAdd, WrappingMul};
  8. use std::cmp::{max, min};
  9. use std::fmt::Debug;
  10. use test::{black_box, Bencher};
  11. // --- Utilities for RNG ----------------------------------------------------
  12. trait BenchInteger: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {}
  13. impl<T> BenchInteger for T where T: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {}
  14. // Simple PRNG so we don't have to worry about rand compatibility
  15. fn lcg<T>(x: T) -> T
  16. where
  17. u32: AsPrimitive<T>,
  18. T: BenchInteger,
  19. {
  20. // LCG parameters from Numerical Recipes
  21. // (but we're applying it to arbitrary sizes)
  22. const LCG_A: u32 = 1664525;
  23. const LCG_C: u32 = 1013904223;
  24. x.wrapping_mul(&LCG_A.as_()).wrapping_add(&LCG_C.as_())
  25. }
  26. // --- Alt. Implementations -------------------------------------------------
  27. trait NaiveAverage {
  28. fn naive_average_ceil(&self, other: &Self) -> Self;
  29. fn naive_average_floor(&self, other: &Self) -> Self;
  30. }
  31. trait UncheckedAverage {
  32. fn unchecked_average_ceil(&self, other: &Self) -> Self;
  33. fn unchecked_average_floor(&self, other: &Self) -> Self;
  34. }
  35. trait ModuloAverage {
  36. fn modulo_average_ceil(&self, other: &Self) -> Self;
  37. fn modulo_average_floor(&self, other: &Self) -> Self;
  38. }
  39. macro_rules! naive_average {
  40. ($T:ident) => {
  41. impl super::NaiveAverage for $T {
  42. fn naive_average_floor(&self, other: &$T) -> $T {
  43. match self.checked_add(*other) {
  44. Some(z) => Integer::div_floor(&z, &2),
  45. None => {
  46. if self > other {
  47. let diff = self - other;
  48. other + Integer::div_floor(&diff, &2)
  49. } else {
  50. let diff = other - self;
  51. self + Integer::div_floor(&diff, &2)
  52. }
  53. }
  54. }
  55. }
  56. fn naive_average_ceil(&self, other: &$T) -> $T {
  57. match self.checked_add(*other) {
  58. Some(z) => Integer::div_ceil(&z, &2),
  59. None => {
  60. if self > other {
  61. let diff = self - other;
  62. self - Integer::div_floor(&diff, &2)
  63. } else {
  64. let diff = other - self;
  65. other - Integer::div_floor(&diff, &2)
  66. }
  67. }
  68. }
  69. }
  70. }
  71. };
  72. }
  73. macro_rules! unchecked_average {
  74. ($T:ident) => {
  75. impl super::UncheckedAverage for $T {
  76. fn unchecked_average_floor(&self, other: &$T) -> $T {
  77. self.wrapping_add(*other) / 2
  78. }
  79. fn unchecked_average_ceil(&self, other: &$T) -> $T {
  80. (self.wrapping_add(*other) / 2).wrapping_add(1)
  81. }
  82. }
  83. };
  84. }
  85. macro_rules! modulo_average {
  86. ($T:ident) => {
  87. impl super::ModuloAverage for $T {
  88. fn modulo_average_ceil(&self, other: &$T) -> $T {
  89. let (q1, r1) = self.div_mod_floor(&2);
  90. let (q2, r2) = other.div_mod_floor(&2);
  91. q1 + q2 + (r1 | r2)
  92. }
  93. fn modulo_average_floor(&self, other: &$T) -> $T {
  94. let (q1, r1) = self.div_mod_floor(&2);
  95. let (q2, r2) = other.div_mod_floor(&2);
  96. q1 + q2 + (r1 * r2)
  97. }
  98. }
  99. };
  100. }
  101. // --- Bench functions ------------------------------------------------------
  102. fn bench_unchecked<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
  103. where
  104. T: Integer + Debug + Copy,
  105. F: Fn(&T, &T) -> T,
  106. {
  107. b.iter(|| {
  108. for (x, y) in v {
  109. black_box(f(x, y));
  110. }
  111. });
  112. }
  113. fn bench_ceil<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
  114. where
  115. T: Integer + Debug + Copy,
  116. F: Fn(&T, &T) -> T,
  117. {
  118. for &(i, j) in v {
  119. let rt = f(&i, &j);
  120. let (a, b) = (min(i, j), max(i, j));
  121. // if both number are the same sign, check rt is in the middle
  122. if (a < T::zero()) == (b < T::zero()) {
  123. if (b - a).is_even() {
  124. assert_eq!(rt - a, b - rt);
  125. } else {
  126. assert_eq!(rt - a, b - rt + T::one());
  127. }
  128. // if both number have a different sign,
  129. } else {
  130. if (a + b).is_even() {
  131. assert_eq!(rt, (a + b) / (T::one() + T::one()))
  132. } else {
  133. assert_eq!(rt, (a + b + T::one()) / (T::one() + T::one()))
  134. }
  135. }
  136. }
  137. bench_unchecked(b, v, f);
  138. }
  139. fn bench_floor<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
  140. where
  141. T: Integer + Debug + Copy,
  142. F: Fn(&T, &T) -> T,
  143. {
  144. for &(i, j) in v {
  145. let rt = f(&i, &j);
  146. let (a, b) = (min(i, j), max(i, j));
  147. // if both number are the same sign, check rt is in the middle
  148. if (a < T::zero()) == (b < T::zero()) {
  149. if (b - a).is_even() {
  150. assert_eq!(rt - a, b - rt);
  151. } else {
  152. assert_eq!(rt - a + T::one(), b - rt);
  153. }
  154. // if both number have a different sign,
  155. } else {
  156. if (a + b).is_even() {
  157. assert_eq!(rt, (a + b) / (T::one() + T::one()))
  158. } else {
  159. assert_eq!(rt, (a + b - T::one()) / (T::one() + T::one()))
  160. }
  161. }
  162. }
  163. bench_unchecked(b, v, f);
  164. }
  165. // --- Bench implementation -------------------------------------------------
  166. macro_rules! bench_average {
  167. ($($T:ident),*) => {$(
  168. mod $T {
  169. use test::Bencher;
  170. use num_integer::{Average, Integer};
  171. use super::{UncheckedAverage, NaiveAverage, ModuloAverage};
  172. use super::{bench_ceil, bench_floor, bench_unchecked};
  173. naive_average!($T);
  174. unchecked_average!($T);
  175. modulo_average!($T);
  176. const SIZE: $T = 30;
  177. fn overflowing() -> Vec<($T, $T)> {
  178. (($T::max_value()-SIZE)..$T::max_value())
  179. .flat_map(|x| -> Vec<_> {
  180. (($T::max_value()-100)..($T::max_value()-100+SIZE))
  181. .map(|y| (x, y))
  182. .collect()
  183. })
  184. .collect()
  185. }
  186. fn small() -> Vec<($T, $T)> {
  187. (0..SIZE)
  188. .flat_map(|x| -> Vec<_> {(0..SIZE).map(|y| (x, y)).collect()})
  189. .collect()
  190. }
  191. fn rand() -> Vec<($T, $T)> {
  192. small()
  193. .into_iter()
  194. .map(|(x, y)| (super::lcg(x), super::lcg(y)))
  195. .collect()
  196. }
  197. mod ceil {
  198. use super::*;
  199. mod small {
  200. use super::*;
  201. #[bench]
  202. fn optimized(b: &mut Bencher) {
  203. let v = small();
  204. bench_ceil(b, &v, |x: &$T, y: &$T| x.average_ceil(y));
  205. }
  206. #[bench]
  207. fn naive(b: &mut Bencher) {
  208. let v = small();
  209. bench_ceil(b, &v, |x: &$T, y: &$T| x.naive_average_ceil(y));
  210. }
  211. #[bench]
  212. fn unchecked(b: &mut Bencher) {
  213. let v = small();
  214. bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_ceil(y));
  215. }
  216. #[bench]
  217. fn modulo(b: &mut Bencher) {
  218. let v = small();
  219. bench_ceil(b, &v, |x: &$T, y: &$T| x.modulo_average_ceil(y));
  220. }
  221. }
  222. mod overflowing {
  223. use super::*;
  224. #[bench]
  225. fn optimized(b: &mut Bencher) {
  226. let v = overflowing();
  227. bench_ceil(b, &v, |x: &$T, y: &$T| x.average_ceil(y));
  228. }
  229. #[bench]
  230. fn naive(b: &mut Bencher) {
  231. let v = overflowing();
  232. bench_ceil(b, &v, |x: &$T, y: &$T| x.naive_average_ceil(y));
  233. }
  234. #[bench]
  235. fn unchecked(b: &mut Bencher) {
  236. let v = overflowing();
  237. bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_ceil(y));
  238. }
  239. #[bench]
  240. fn modulo(b: &mut Bencher) {
  241. let v = overflowing();
  242. bench_ceil(b, &v, |x: &$T, y: &$T| x.modulo_average_ceil(y));
  243. }
  244. }
  245. mod rand {
  246. use super::*;
  247. #[bench]
  248. fn optimized(b: &mut Bencher) {
  249. let v = rand();
  250. bench_ceil(b, &v, |x: &$T, y: &$T| x.average_ceil(y));
  251. }
  252. #[bench]
  253. fn naive(b: &mut Bencher) {
  254. let v = rand();
  255. bench_ceil(b, &v, |x: &$T, y: &$T| x.naive_average_ceil(y));
  256. }
  257. #[bench]
  258. fn unchecked(b: &mut Bencher) {
  259. let v = rand();
  260. bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_ceil(y));
  261. }
  262. #[bench]
  263. fn modulo(b: &mut Bencher) {
  264. let v = rand();
  265. bench_ceil(b, &v, |x: &$T, y: &$T| x.modulo_average_ceil(y));
  266. }
  267. }
  268. }
  269. mod floor {
  270. use super::*;
  271. mod small {
  272. use super::*;
  273. #[bench]
  274. fn optimized(b: &mut Bencher) {
  275. let v = small();
  276. bench_floor(b, &v, |x: &$T, y: &$T| x.average_floor(y));
  277. }
  278. #[bench]
  279. fn naive(b: &mut Bencher) {
  280. let v = small();
  281. bench_floor(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
  282. }
  283. #[bench]
  284. fn unchecked(b: &mut Bencher) {
  285. let v = small();
  286. bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
  287. }
  288. #[bench]
  289. fn modulo(b: &mut Bencher) {
  290. let v = small();
  291. bench_floor(b, &v, |x: &$T, y: &$T| x.modulo_average_floor(y));
  292. }
  293. }
  294. mod overflowing {
  295. use super::*;
  296. #[bench]
  297. fn optimized(b: &mut Bencher) {
  298. let v = overflowing();
  299. bench_floor(b, &v, |x: &$T, y: &$T| x.average_floor(y));
  300. }
  301. #[bench]
  302. fn naive(b: &mut Bencher) {
  303. let v = overflowing();
  304. bench_floor(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
  305. }
  306. #[bench]
  307. fn unchecked(b: &mut Bencher) {
  308. let v = overflowing();
  309. bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
  310. }
  311. #[bench]
  312. fn modulo(b: &mut Bencher) {
  313. let v = overflowing();
  314. bench_floor(b, &v, |x: &$T, y: &$T| x.modulo_average_floor(y));
  315. }
  316. }
  317. mod rand {
  318. use super::*;
  319. #[bench]
  320. fn optimized(b: &mut Bencher) {
  321. let v = rand();
  322. bench_floor(b, &v, |x: &$T, y: &$T| x.average_floor(y));
  323. }
  324. #[bench]
  325. fn naive(b: &mut Bencher) {
  326. let v = rand();
  327. bench_floor(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
  328. }
  329. #[bench]
  330. fn unchecked(b: &mut Bencher) {
  331. let v = rand();
  332. bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
  333. }
  334. #[bench]
  335. fn modulo(b: &mut Bencher) {
  336. let v = rand();
  337. bench_floor(b, &v, |x: &$T, y: &$T| x.modulo_average_floor(y));
  338. }
  339. }
  340. }
  341. }
  342. )*}
  343. }
  344. bench_average!(i8, i16, i32, i64, i128, isize);
  345. bench_average!(u8, u16, u32, u64, u128, usize);