|
1 |
| -//! This benchmarks the `Integer::isqrt` methods. |
2 |
| -
|
3 |
| -macro_rules! benches { |
4 |
| - ($($T:ident)+) => { |
5 |
| - $( |
6 |
| - mod $T { |
7 |
| - use test::{black_box, Bencher}; |
8 |
| - |
9 |
| - // Benchmark the square roots of: |
10 |
| - // |
11 |
| - // * the first 1,024 perfect squares |
12 |
| - // * halfway between each of the first 1,024 perfect squares |
13 |
| - // and the next perfect square |
14 |
| - // * the next perfect square after the each of the first 1,024 |
15 |
| - // perfect squares, minus one |
16 |
| - // * the last 1,024 perfect squares |
17 |
| - // * the last 1,024 perfect squares, minus one |
18 |
| - // * halfway between each of the last 1,024 perfect squares |
19 |
| - // and the previous perfect square |
20 |
| - #[bench] |
21 |
| - fn isqrt(bench: &mut Bencher) { |
22 |
| - let mut inputs = Vec::with_capacity(6 * 1_024); |
23 |
| - |
24 |
| - // The inputs to benchmark are worked out by using the fact |
25 |
| - // that the nth nonzero perfect square is the sum of the |
26 |
| - // first n odd numbers: |
27 |
| - // |
28 |
| - // 1 = 1 |
29 |
| - // 4 = 1 + 3 |
30 |
| - // 9 = 1 + 3 + 5 |
31 |
| - // 16 = 1 + 3 + 5 + 7 |
32 |
| - // |
33 |
| - // Note also that the last odd number added in is two times |
34 |
| - // the square root of the previous perfect square, plus |
35 |
| - // one: |
36 |
| - // |
37 |
| - // 1 = 2*0 + 1 |
38 |
| - // 3 = 2*1 + 1 |
39 |
| - // 5 = 2*2 + 1 |
40 |
| - // 7 = 2*3 + 1 |
41 |
| - // |
42 |
| - // That means we can add the square root of this perfect |
43 |
| - // square once to get about halfway to the next perfect |
44 |
| - // square, then we can add the square root of this perfect |
45 |
| - // square again to get to the next perfect square minus |
46 |
| - // one, then we can add one to get to the next perfect |
47 |
| - // square. |
48 |
| - // |
49 |
| - // Here we include, for each of the first 1,024 perfect |
50 |
| - // squares: |
51 |
| - // |
52 |
| - // * the current perfect square |
53 |
| - // * about halfway to the next perfect square |
54 |
| - // * the next perfect square, minus one |
55 |
| - let mut n: $T = 0; |
56 |
| - for sqrt_n in 0..1_024.min((1_u128 << (($T::BITS - $T::MAX.leading_zeros())/2)) - 1) as $T { |
57 |
| - inputs.push(n); |
58 |
| - n += sqrt_n; |
59 |
| - inputs.push(n); |
60 |
| - n += sqrt_n; |
61 |
| - inputs.push(n); |
62 |
| - n += 1; |
| 1 | +use rand::Rng; |
| 2 | +use test::{black_box, Bencher}; |
| 3 | + |
| 4 | +macro_rules! int_sqrt_bench { |
| 5 | + ($t:ty, $predictable:ident, $random:ident, $random_small:ident) => { |
| 6 | + #[bench] |
| 7 | + fn $predictable(bench: &mut Bencher) { |
| 8 | + bench.iter(|| { |
| 9 | + for n in 0..(<$t>::BITS / 8) { |
| 10 | + for i in 1..=(100 as $t) { |
| 11 | + let x = black_box(i << (n * 8)); |
| 12 | + black_box(x.isqrt()); |
63 | 13 | }
|
64 |
| - |
65 |
| - // Similarly, we include, for each of the last 1,024 |
66 |
| - // perfect squares: |
67 |
| - // |
68 |
| - // * the current perfect square |
69 |
| - // * the current perfect square, minus one |
70 |
| - // * about halfway to the previous perfect square |
71 |
| - let maximum_sqrt = $T::MAX.isqrt(); |
72 |
| - let mut n = maximum_sqrt * maximum_sqrt; |
73 |
| - |
74 |
| - for sqrt_n in (maximum_sqrt - 1_024.min((1_u128 << (($T::BITS - 1)/2)) - 1) as $T..maximum_sqrt).rev() { |
75 |
| - inputs.push(n); |
76 |
| - n -= 1; |
77 |
| - inputs.push(n); |
78 |
| - n -= sqrt_n; |
79 |
| - inputs.push(n); |
80 |
| - n -= sqrt_n; |
81 |
| - } |
82 |
| - |
83 |
| - bench.iter(|| { |
84 |
| - for x in &inputs { |
85 |
| - black_box(black_box(x).isqrt()); |
86 |
| - } |
87 |
| - }); |
88 | 14 | }
|
89 |
| - } |
90 |
| - )* |
91 |
| - }; |
92 |
| -} |
93 |
| - |
94 |
| -macro_rules! push_n { |
95 |
| - ($T:ident, $inputs:ident, $n:ident) => { |
96 |
| - if $n != 0 { |
97 |
| - $inputs.push( |
98 |
| - core::num::$T::new($n) |
99 |
| - .expect("Cannot create a new `NonZero` value from a nonzero value"), |
100 |
| - ); |
| 15 | + }); |
101 | 16 | }
|
102 |
| - }; |
103 |
| -} |
104 |
| - |
105 |
| -macro_rules! nonzero_benches { |
106 |
| - ($mod:ident $T:ident $RegularT:ident) => { |
107 |
| - mod $mod { |
108 |
| - use test::{black_box, Bencher}; |
109 |
| - |
110 |
| - // Benchmark the square roots of: |
111 |
| - // |
112 |
| - // * the first 1,024 perfect squares |
113 |
| - // * halfway between each of the first 1,024 perfect squares |
114 |
| - // and the next perfect square |
115 |
| - // * the next perfect square after the each of the first 1,024 |
116 |
| - // perfect squares, minus one |
117 |
| - // * the last 1,024 perfect squares |
118 |
| - // * the last 1,024 perfect squares, minus one |
119 |
| - // * halfway between each of the last 1,024 perfect squares |
120 |
| - // and the previous perfect square |
121 |
| - #[bench] |
122 |
| - fn isqrt(bench: &mut Bencher) { |
123 |
| - let mut inputs: Vec<core::num::$T> = Vec::with_capacity(6 * 1_024); |
124 | 17 |
|
125 |
| - // The inputs to benchmark are worked out by using the fact |
126 |
| - // that the nth nonzero perfect square is the sum of the |
127 |
| - // first n odd numbers: |
128 |
| - // |
129 |
| - // 1 = 1 |
130 |
| - // 4 = 1 + 3 |
131 |
| - // 9 = 1 + 3 + 5 |
132 |
| - // 16 = 1 + 3 + 5 + 7 |
133 |
| - // |
134 |
| - // Note also that the last odd number added in is two times |
135 |
| - // the square root of the previous perfect square, plus |
136 |
| - // one: |
137 |
| - // |
138 |
| - // 1 = 2*0 + 1 |
139 |
| - // 3 = 2*1 + 1 |
140 |
| - // 5 = 2*2 + 1 |
141 |
| - // 7 = 2*3 + 1 |
142 |
| - // |
143 |
| - // That means we can add the square root of this perfect |
144 |
| - // square once to get about halfway to the next perfect |
145 |
| - // square, then we can add the square root of this perfect |
146 |
| - // square again to get to the next perfect square minus |
147 |
| - // one, then we can add one to get to the next perfect |
148 |
| - // square. |
149 |
| - // |
150 |
| - // Here we include, for each of the first 1,024 perfect |
151 |
| - // squares: |
152 |
| - // |
153 |
| - // * the current perfect square |
154 |
| - // * about halfway to the next perfect square |
155 |
| - // * the next perfect square, minus one |
156 |
| - let mut n: $RegularT = 0; |
157 |
| - for sqrt_n in 0..1_024 |
158 |
| - .min((1_u128 << (($RegularT::BITS - $RegularT::MAX.leading_zeros()) / 2)) - 1) |
159 |
| - as $RegularT |
160 |
| - { |
161 |
| - push_n!($T, inputs, n); |
162 |
| - n += sqrt_n; |
163 |
| - push_n!($T, inputs, n); |
164 |
| - n += sqrt_n; |
165 |
| - push_n!($T, inputs, n); |
166 |
| - n += 1; |
| 18 | + #[bench] |
| 19 | + fn $random(bench: &mut Bencher) { |
| 20 | + let mut rng = crate::bench_rng(); |
| 21 | + /* Exponentially distributed random numbers from the whole range of the type. */ |
| 22 | + let numbers: Vec<$t> = (0..256) |
| 23 | + .map(|_| { |
| 24 | + let x = rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS); |
| 25 | + if x != 0 { x } else { 1 } |
| 26 | + }) |
| 27 | + .collect(); |
| 28 | + bench.iter(|| { |
| 29 | + for x in &numbers { |
| 30 | + black_box(black_box(x).isqrt()); |
167 | 31 | }
|
| 32 | + }); |
| 33 | + } |
168 | 34 |
|
169 |
| - // Similarly, we include, for each of the last 1,024 |
170 |
| - // perfect squares: |
171 |
| - // |
172 |
| - // * the current perfect square |
173 |
| - // * the current perfect square, minus one |
174 |
| - // * about halfway to the previous perfect square |
175 |
| - let maximum_sqrt = $RegularT::MAX.isqrt(); |
176 |
| - let mut n = maximum_sqrt * maximum_sqrt; |
177 |
| - |
178 |
| - for sqrt_n in (maximum_sqrt |
179 |
| - - 1_024.min((1_u128 << (($RegularT::BITS - 1) / 2)) - 1) as $RegularT |
180 |
| - ..maximum_sqrt) |
181 |
| - .rev() |
182 |
| - { |
183 |
| - push_n!($T, inputs, n); |
184 |
| - n -= 1; |
185 |
| - push_n!($T, inputs, n); |
186 |
| - n -= sqrt_n; |
187 |
| - push_n!($T, inputs, n); |
188 |
| - n -= sqrt_n; |
| 35 | + #[bench] |
| 36 | + fn $random_small(bench: &mut Bencher) { |
| 37 | + let mut rng = crate::bench_rng(); |
| 38 | + /* Exponentially distributed random numbers from the range 0..256. */ |
| 39 | + let numbers: Vec<$t> = (0..256) |
| 40 | + .map(|_| { |
| 41 | + let x = (rng.gen::<u8>() >> rng.gen_range(0..u8::BITS)) as $t; |
| 42 | + if x != 0 { x } else { 1 } |
| 43 | + }) |
| 44 | + .collect(); |
| 45 | + bench.iter(|| { |
| 46 | + for x in &numbers { |
| 47 | + black_box(black_box(x).isqrt()); |
189 | 48 | }
|
190 |
| - |
191 |
| - bench.iter(|| { |
192 |
| - for n in &inputs { |
193 |
| - black_box(black_box(n).isqrt()); |
194 |
| - } |
195 |
| - }); |
196 |
| - } |
| 49 | + }); |
197 | 50 | }
|
198 | 51 | };
|
199 | 52 | }
|
200 | 53 |
|
201 |
| -benches!(i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize); |
202 |
| -nonzero_benches!(non_zero_u8 NonZeroU8 u8); |
203 |
| -nonzero_benches!(non_zero_u16 NonZeroU16 u16); |
204 |
| -nonzero_benches!(non_zero_u32 NonZeroU32 u32); |
205 |
| -nonzero_benches!(non_zero_u64 NonZeroU64 u64); |
206 |
| -nonzero_benches!(non_zero_u128 NonZeroU128 u128); |
207 |
| -nonzero_benches!(non_zero_usize NonZeroUsize usize); |
| 54 | +int_sqrt_bench! {u8, u8_sqrt_predictable, u8_sqrt_random, u8_sqrt_random_small} |
| 55 | +int_sqrt_bench! {u16, u16_sqrt_predictable, u16_sqrt_random, u16_sqrt_random_small} |
| 56 | +int_sqrt_bench! {u32, u32_sqrt_predictable, u32_sqrt_random, u32_sqrt_random_small} |
| 57 | +int_sqrt_bench! {u64, u64_sqrt_predictable, u64_sqrt_random, u64_sqrt_random_small} |
| 58 | +int_sqrt_bench! {u128, u128_sqrt_predictable, u128_sqrt_random, u128_sqrt_random_small} |
0 commit comments