|
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