Skip to content

Commit d180572

Browse files
committed
Added benchmarks for isqrt implementations
1 parent 8859812 commit d180572

File tree

3 files changed

+209
-0
lines changed

3 files changed

+209
-0
lines changed

library/core/benches/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#![feature(iter_array_chunks)]
99
#![feature(iter_next_chunk)]
1010
#![feature(iter_advance_by)]
11+
#![feature(isqrt)]
1112

1213
extern crate test;
1314

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
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;
63+
}
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+
}
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+
);
101+
}
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+
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;
167+
}
168+
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;
189+
}
190+
191+
bench.iter(|| {
192+
for n in &inputs {
193+
black_box(black_box(n).isqrt());
194+
}
195+
});
196+
}
197+
}
198+
};
199+
}
200+
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);

library/core/benches/num/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod dec2flt;
22
mod flt2dec;
33
mod int_log;
44
mod int_pow;
5+
mod int_sqrt;
56

67
use std::str::FromStr;
78
use test::{black_box, Bencher};

0 commit comments

Comments
 (0)