Skip to content

Commit 7581fcb

Browse files
committed
Refactor benches/average.rs and check results of methods that should be exact
1 parent 5ae9c3e commit 7581fcb

File tree

1 file changed

+191
-73
lines changed

1 file changed

+191
-73
lines changed

benches/average.rs

Lines changed: 191 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,18 @@ extern crate num_integer;
66
extern crate num_traits;
77
extern crate test;
88

9+
use std::cmp::{min, max};
10+
use std::fmt::Debug;
911
use num_integer::Integer;
1012
use num_traits::{AsPrimitive, PrimInt, WrappingAdd, WrappingMul};
1113
use test::{black_box, Bencher};
1214

15+
// --- Utilities for RNG ----------------------------------------------------
16+
1317
trait BenchInteger: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {}
1418

1519
impl<T> BenchInteger for T where T: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {}
1620

17-
trait NaiveAverage {
18-
fn naive_average_floor(&self, other: &Self) -> Self;
19-
fn naive_average_ceil(&self, other: &Self) -> Self;
20-
}
21-
22-
trait UncheckedAverage {
23-
fn unchecked_average_floor(&self, other: &Self) -> Self;
24-
fn unchecked_average_ceil(&self, other: &Self) -> Self;
25-
}
26-
27-
fn bench<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
28-
where
29-
T: Integer,
30-
F: Fn(&T, &T) -> T,
31-
{
32-
b.iter(|| {
33-
for (x, y) in v {
34-
black_box(f(x, y));
35-
}
36-
});
37-
}
38-
3921
// Simple PRNG so we don't have to worry about rand compatibility
4022
fn lcg<T>(x: T) -> T
4123
where
@@ -49,26 +31,43 @@ where
4931
x.wrapping_mul(&LCG_A.as_()).wrapping_add(&LCG_C.as_())
5032
}
5133

34+
// --- Alt. Implementations -------------------------------------------------
35+
36+
trait NaiveAverage {
37+
fn naive_average_ceil(&self, other: &Self) -> Self;
38+
fn naive_average_floor(&self, other: &Self) -> Self;
39+
}
40+
41+
trait UncheckedAverage {
42+
fn unchecked_average_ceil(&self, other: &Self) -> Self;
43+
fn unchecked_average_floor(&self, other: &Self) -> Self;
44+
}
45+
46+
trait ModuloAverage {
47+
fn modulo_average_ceil(&self, other: &Self) -> Self;
48+
fn modulo_average_floor(&self, other: &Self) -> Self;
49+
}
50+
5251
macro_rules! naive_average {
5352
($T:ident) => {
5453
impl super::NaiveAverage for $T {
5554
fn naive_average_floor(&self, other: &$T) -> $T {
5655
match self.checked_add(*other) {
57-
Some(z) => z / 2,
56+
Some(z) => z.div_floor(&2),
5857
None => {
5958
if self > other {
6059
let diff = self - other;
61-
other + diff / 2
60+
other + diff.div_floor(&2)
6261
} else {
6362
let diff = other - self;
64-
self + diff / 2
63+
self + diff.div_floor(&2)
6564
}
6665
}
6766
}
6867
}
6968
fn naive_average_ceil(&self, other: &$T) -> $T {
7069
match self.checked_add(*other).and_then(|x| x.checked_add(1)) {
71-
Some(z) => z / 2,
70+
Some(z) => z.div_ceil(&2),
7271
None => {
7372
if self > other {
7473
let diff = self - other;
@@ -97,16 +96,95 @@ macro_rules! unchecked_average {
9796
};
9897
}
9998

99+
macro_rules! modulo_average {
100+
($T:ident) => {
101+
impl super::ModuloAverage for $T {
102+
fn modulo_average_ceil(&self, other: &$T) -> $T {
103+
let (q1, r1) = self.div_mod_floor(&2);
104+
let (q2, r2) = other.div_mod_floor(&2);
105+
q1 + q2 + (r1 * r2)
106+
}
107+
fn modulo_average_floor(&self, other: &$T) -> $T {
108+
let (q1, r1) = self.div_mod_floor(&2);
109+
let (q2, r2) = other.div_mod_floor(&2);
110+
q1 + q2 + (r1 * r2)
111+
}
112+
}
113+
};
114+
}
115+
116+
// --- Bench functions ------------------------------------------------------
117+
118+
fn bench_unchecked<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
119+
where
120+
T: Integer + Debug + Copy,
121+
F: Fn(&T, &T) -> T,
122+
{
123+
b.iter(|| {
124+
for (x, y) in v {
125+
black_box(f(x, y));
126+
}
127+
});
128+
}
129+
130+
fn bench_ceil<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
131+
where
132+
T: Integer + Debug + Copy,
133+
F: Fn(&T, &T) -> T,
134+
{
135+
for &(i, j) in v {
136+
let rt = f(&i, &j);
137+
let (a, b) = (min(i, j), max(i, j));
138+
if (b - a).is_even() {
139+
assert_eq!(rt - a, b - rt);
140+
} else {
141+
assert_eq!(rt - a, b - rt + T::one());
142+
}
143+
}
144+
bench_unchecked(b, v, f);
145+
}
146+
147+
fn bench_floor<T, F>(b: &mut Bencher, v: &[(T, T)], f: F)
148+
where
149+
T: Integer + Debug + Copy,
150+
F: Fn(&T, &T) -> T,
151+
{
152+
for &(i, j) in v {
153+
let rt = f(&i, &j);
154+
let (a, b) = (min(i, j), max(i, j));
155+
println!("{:?} + {:?} / 2 = {:?}", a, b, rt);
156+
// if both number are the same sign, check rt is in the middle
157+
if (a < T::zero()) == (b < T::zero()) {
158+
if (b - a).is_even() {
159+
assert_eq!(rt - a, b - rt);
160+
} else {
161+
assert_eq!(rt - a + T::one(), b - rt);
162+
}
163+
// if both number have a different sign,
164+
} else {
165+
if (a + b).is_even() {
166+
assert_eq!(rt, (a + b) / (T::one() + T::one()))
167+
} else {
168+
assert_eq!(rt, (a + b - T::one()) / (T::one() + T::one()))
169+
}
170+
}
171+
}
172+
bench_unchecked(b, v, f);
173+
}
174+
175+
// --- Bench implementation -------------------------------------------------
176+
100177
macro_rules! bench_average {
101178
($($T:ident),*) => {$(
102179
mod $T {
103180
use test::Bencher;
104-
use num_integer::Average;
105-
use UncheckedAverage;
106-
use NaiveAverage;
181+
use num_integer::{Average, Integer};
182+
use super::{UncheckedAverage, NaiveAverage, ModuloAverage};
183+
use super::{bench_ceil, bench_floor, bench_unchecked};
107184

108185
naive_average!($T);
109186
unchecked_average!($T);
187+
modulo_average!($T);
110188

111189
const SIZE: $T = 30;
112190

@@ -133,59 +211,99 @@ macro_rules! bench_average {
133211
.collect()
134212
}
135213

136-
#[bench]
137-
fn average_floor_small(b: &mut Bencher) {
138-
let v = small();
139-
super::bench(b, &v, |x: &$T, y: &$T| x.average_floor(y));
140-
}
214+
mod floor {
141215

142-
#[bench]
143-
fn average_floor_small_naive(b: &mut Bencher) {
144-
let v = small();
145-
super::bench(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
146-
}
216+
use super::*;
147217

148-
#[bench]
149-
fn average_floor_small_unchecked(b: &mut Bencher) {
150-
let v = small();
151-
super::bench(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
152-
}
218+
mod small {
153219

154-
#[bench]
155-
fn average_floor_overflowing(b: &mut Bencher) {
156-
let v = overflowing();
157-
super::bench(b, &v, |x: &$T, y: &$T| x.average_floor(y));
158-
}
220+
use super::*;
159221

160-
#[bench]
161-
fn average_floor_overflowing_naive(b: &mut Bencher) {
162-
let v = overflowing();
163-
super::bench(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
164-
}
222+
#[bench]
223+
fn optimized(b: &mut Bencher) {
224+
let v = small();
225+
bench_floor(b, &v, |x: &$T, y: &$T| x.average_floor(y));
226+
}
165227

166-
#[bench]
167-
fn average_floor_overflowing_unchecked(b: &mut Bencher) {
168-
let v = overflowing();
169-
super::bench(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
170-
}
228+
#[bench]
229+
fn naive(b: &mut Bencher) {
230+
let v = small();
231+
bench_floor(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
232+
}
171233

172-
#[bench]
173-
fn average_floor_rand(b: &mut Bencher) {
174-
let v = rand();
175-
super::bench(b, &v, |x: &$T, y: &$T| x.average_floor(y));
176-
}
234+
#[bench]
235+
fn unchecked(b: &mut Bencher) {
236+
let v = small();
237+
bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
238+
}
177239

178-
#[bench]
179-
fn average_floor_rand_naive(b: &mut Bencher) {
180-
let v = rand();
181-
super::bench(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
182-
}
240+
#[bench]
241+
fn modulo(b: &mut Bencher) {
242+
let v = small();
243+
bench_floor(b, &v, |x: &$T, y: &$T| x.modulo_average_floor(y));
244+
}
245+
}
246+
247+
mod overflowing {
248+
249+
use super::*;
250+
251+
#[bench]
252+
fn optimized(b: &mut Bencher) {
253+
let v = overflowing();
254+
bench_floor(b, &v, |x: &$T, y: &$T| x.average_floor(y));
255+
}
256+
257+
#[bench]
258+
fn naive(b: &mut Bencher) {
259+
let v = overflowing();
260+
bench_floor(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
261+
}
262+
263+
#[bench]
264+
fn unchecked(b: &mut Bencher) {
265+
let v = overflowing();
266+
bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
267+
}
268+
269+
#[bench]
270+
fn modulo(b: &mut Bencher) {
271+
let v = overflowing();
272+
bench_floor(b, &v, |x: &$T, y: &$T| x.modulo_average_floor(y));
273+
}
274+
}
275+
276+
mod rand {
277+
278+
use super::*;
279+
280+
#[bench]
281+
fn optimized(b: &mut Bencher) {
282+
let v = rand();
283+
bench_floor(b, &v, |x: &$T, y: &$T| x.average_floor(y));
284+
}
285+
286+
#[bench]
287+
fn naive(b: &mut Bencher) {
288+
let v = rand();
289+
bench_floor(b, &v, |x: &$T, y: &$T| x.naive_average_floor(y));
290+
}
291+
292+
#[bench]
293+
fn unchecked(b: &mut Bencher) {
294+
let v = rand();
295+
bench_unchecked(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
296+
}
297+
298+
#[bench]
299+
fn modulo(b: &mut Bencher) {
300+
let v = rand();
301+
bench_floor(b, &v, |x: &$T, y: &$T| x.modulo_average_floor(y));
302+
}
303+
}
183304

184-
#[bench]
185-
fn average_floor_rand_unchecked(b: &mut Bencher) {
186-
let v = rand();
187-
super::bench(b, &v, |x: &$T, y: &$T| x.unchecked_average_floor(y));
188305
}
306+
189307
}
190308
)*}
191309
}

0 commit comments

Comments
 (0)