Skip to content

Commit 86e019b

Browse files
bors[bot]cuviper
andcommitted
Merge #53
53: switch gen_biguint to fill_bytes r=cuviper a=TheIronBorn Changes `gen_biguint` from a `push(gen::<u32>)` method to rand's [`fill_bytes`](https://docs.rs/rand/0.5.0/rand/trait.RngCore.html#tymethod.fill_bytes). This should improve performance in most cases. - For small PRNGs which only natively generate 64 bits (like Xorshift64 or [`splitmix64.c`](http://prng.di.unimi.it/splitmix64.c)), this will no longer throw away half the bits generated. - For block PRNGs like `StdRng`, this should reduce overhead. - For an SIMD PRNG (rust-random/rand#377), this would be a significant improvement. ```diff,ignore name no_fill ns/iter fill ns/iter diff ns/iter diff % speedup +rand_1009 256 222 -34 -13.28% x 1.15 +rand_131072 27,366 14,715 -12,651 -46.23% x 1.86 +rand_2048 459 357 -102 -22.22% x 1.29 -rand_256 93 130 37 39.78% x 0.72 +rand_4096 842 557 -285 -33.85% x 1.51 -rand_64 69 92 23 33.33% x 0.75 +rand_65536 13,625 7,382 -6,243 -45.82% x 1.85 +rand_8192 1,836 869 -967 -52.67% x 2.11 ``` (i.e. `rand_1009` does `gen_biguint(1009)`. All benches are powers of two except `rand_1009`) (Let me know if you want the `rand_` benches added) Co-authored-by: TheIronBorn <> Co-authored-by: Josh Stone <cuviper@gmail.com>
2 parents 8916248 + 8b5a092 commit 86e019b

File tree

3 files changed

+197
-8
lines changed

3 files changed

+197
-8
lines changed

benches/bigint.rs

+46
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,52 @@ fn from_str_radix_36(b: &mut Bencher) {
214214
from_str_radix_bench(b, 36);
215215
}
216216

217+
fn rand_bench(b: &mut Bencher, bits: usize) {
218+
let mut rng = get_rng();
219+
220+
b.iter(|| rng.gen_bigint(bits));
221+
}
222+
223+
#[bench]
224+
fn rand_64(b: &mut Bencher) {
225+
rand_bench(b, 1 << 6);
226+
}
227+
228+
#[bench]
229+
fn rand_256(b: &mut Bencher) {
230+
rand_bench(b, 1 << 8);
231+
}
232+
233+
#[bench]
234+
fn rand_1009(b: &mut Bencher) {
235+
rand_bench(b, 1009);
236+
}
237+
238+
#[bench]
239+
fn rand_2048(b: &mut Bencher) {
240+
rand_bench(b, 1 << 11);
241+
}
242+
243+
#[bench]
244+
fn rand_4096(b: &mut Bencher) {
245+
rand_bench(b, 1 << 12);
246+
}
247+
248+
#[bench]
249+
fn rand_8192(b: &mut Bencher) {
250+
rand_bench(b, 1 << 13);
251+
}
252+
253+
#[bench]
254+
fn rand_65536(b: &mut Bencher) {
255+
rand_bench(b, 1 << 16);
256+
}
257+
258+
#[bench]
259+
fn rand_131072(b: &mut Bencher) {
260+
rand_bench(b, 1 << 17);
261+
}
262+
217263
#[bench]
218264
fn shl(b: &mut Bencher) {
219265
let n = BigUint::one() << 1000;

src/bigrand.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use rand::prelude::*;
44
use rand::distributions::uniform::{SampleUniform, UniformSampler};
5+
use rand::AsByteSliceMut;
56

67
use BigInt;
78
use BigUint;
@@ -39,13 +40,15 @@ impl<R: Rng + ?Sized> RandBigInt for R {
3940
fn gen_biguint(&mut self, bit_size: usize) -> BigUint {
4041
use super::big_digit::BITS;
4142
let (digits, rem) = bit_size.div_rem(&BITS);
42-
let mut data = Vec::with_capacity(digits + 1);
43-
for _ in 0..digits {
44-
data.push(self.gen());
45-
}
43+
let mut data = vec![BigDigit::default(); digits + (rem > 0) as usize];
44+
// `fill_bytes` is faster than many `gen::<u32>` calls
45+
self.fill_bytes(data[..].as_byte_slice_mut());
46+
// Swap bytes per the `Rng::fill` source. This might be
47+
// unnecessary if reproducibility across architectures is not
48+
// desired.
49+
data.to_le();
4650
if rem > 0 {
47-
let final_digit: BigDigit = self.gen();
48-
data.push(final_digit >> (BITS - rem));
51+
data[digits] >>= BITS - rem;
4952
}
5053
BigUint::new(data)
5154
}

tests/rand.rs

+142-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ mod biguint {
88
use num_bigint::{BigUint, RandBigInt, RandomBits};
99
use num_traits::Zero;
1010
use rand::thread_rng;
11-
use rand::Rng;
11+
use rand::{Rng, SeedableRng};
1212
use rand::distributions::Uniform;
1313

1414
#[test]
@@ -89,13 +89,83 @@ mod biguint {
8989
assert!(n < u);
9090
}
9191
}
92+
93+
fn seeded_value_stability<R: SeedableRng + RandBigInt>(expected: &[&str]) {
94+
let mut seed = <R::Seed>::default();
95+
for (i, x) in seed.as_mut().iter_mut().enumerate() {
96+
*x = (i as u8).wrapping_mul(191);
97+
}
98+
let mut rng = R::from_seed(seed);
99+
for (i, &s) in expected.iter().enumerate() {
100+
let n: BigUint = s.parse().unwrap();
101+
let r = rng.gen_biguint((1 << i) + i);
102+
assert_eq!(n, r);
103+
}
104+
}
105+
106+
#[test]
107+
fn test_chacha_value_stability() {
108+
const EXPECTED: &[&str] = &[
109+
"0",
110+
"0",
111+
"52",
112+
"84",
113+
"23780",
114+
"86502865016",
115+
"187057847319509867386",
116+
"34045731223080904464438757488196244981910",
117+
"23813754422987836414755953516143692594193066497413249270287126597896871975915808",
118+
"57401636903146945411652549098818446911814352529449356393690984105383482703074355\
119+
67088360974672291353736011718191813678720755501317478656550386324355699624671",
120+
];
121+
use rand::prng::ChaChaRng;
122+
seeded_value_stability::<ChaChaRng>(EXPECTED);
123+
}
124+
125+
#[test]
126+
fn test_isaac_value_stability() {
127+
const EXPECTED: &[&str] = &[
128+
"1",
129+
"4",
130+
"3",
131+
"649",
132+
"89116",
133+
"7730042024",
134+
"20773149082453254949",
135+
"35999009049239918667571895439206839620281",
136+
"10191757312714088681302309313551624007714035309632506837271600807524767413673006",
137+
"37805949268912387809989378008822038725134260145886913321084097194957861133272558\
138+
43458183365174899239251448892645546322463253898288141861183340823194379722556",
139+
];
140+
use rand::prng::IsaacRng;
141+
seeded_value_stability::<IsaacRng>(EXPECTED);
142+
}
143+
144+
#[test]
145+
fn test_xorshift_value_stability() {
146+
const EXPECTED: &[&str] = &[
147+
"1",
148+
"0",
149+
"37",
150+
"395",
151+
"181116",
152+
"122718231117",
153+
"1068467172329355695001",
154+
"28246925743544411614293300167064395633287",
155+
"12750053187017853048648861493745244146555950255549630854523304068318587267293038",
156+
"53041498719137109355568081064978196049094604705283682101683207799515709404788873\
157+
53417136457745727045473194367732849819278740266658219147356315674940229288531",
158+
];
159+
use rand::prng::XorShiftRng;
160+
seeded_value_stability::<XorShiftRng>(EXPECTED);
161+
}
92162
}
93163

94164
mod bigint {
95165
use num_bigint::{BigInt, RandBigInt, RandomBits};
96166
use num_traits::Zero;
97167
use rand::thread_rng;
98-
use rand::Rng;
168+
use rand::{Rng, SeedableRng};
99169
use rand::distributions::Uniform;
100170

101171
#[test]
@@ -181,4 +251,74 @@ mod bigint {
181251
check(-l.clone(), u.clone());
182252
check(-u.clone(), -l.clone());
183253
}
254+
255+
fn seeded_value_stability<R: SeedableRng + RandBigInt>(expected: &[&str]) {
256+
let mut seed = <R::Seed>::default();
257+
for (i, x) in seed.as_mut().iter_mut().enumerate() {
258+
*x = (i as u8).wrapping_mul(191);
259+
}
260+
let mut rng = R::from_seed(seed);
261+
for (i, &s) in expected.iter().enumerate() {
262+
let n: BigInt = s.parse().unwrap();
263+
let r = rng.gen_bigint((1 << i) + i);
264+
assert_eq!(n, r);
265+
}
266+
}
267+
268+
#[test]
269+
fn test_chacha_value_stability() {
270+
const EXPECTED: &[&str] = &[
271+
"0",
272+
"-6",
273+
"-1",
274+
"1321",
275+
"-147247",
276+
"8486373526",
277+
"-272736656290199720696",
278+
"2731152629387534140535423510744221288522",
279+
"-28820024790651190394679732038637785320661450462089347915910979466834461433196572",
280+
"501454570554170484799723603981439288209930393334472085317977614690773821680884844\
281+
8530978478667288338327570972869032358120588620346111979053742269317702532328",
282+
];
283+
use rand::prng::ChaChaRng;
284+
seeded_value_stability::<ChaChaRng>(EXPECTED);
285+
}
286+
287+
#[test]
288+
fn test_isaac_value_stability() {
289+
const EXPECTED: &[&str] = &[
290+
"1",
291+
"0",
292+
"5",
293+
"113",
294+
"-132240",
295+
"-36348760761",
296+
"-365690596708430705434",
297+
"-14090753008246284277803606722552430292432",
298+
"-26313941628626248579319341019368550803676255307056857978955881718727601479436059",
299+
"-14563174552421101848999036239003801073335703811160945137332228646111920972691151\
300+
88341090358094331641182310792892459091016794928947242043358702692294695845817",
301+
];
302+
use rand::prng::IsaacRng;
303+
seeded_value_stability::<IsaacRng>(EXPECTED);
304+
}
305+
306+
#[test]
307+
fn test_xorshift_value_stability() {
308+
const EXPECTED: &[&str] = &[
309+
"-1",
310+
"-4",
311+
"11",
312+
"-1802",
313+
"966495",
314+
"-62592045703",
315+
"-602281783447192077116",
316+
"-34335811410223060575607987996861632509125",
317+
"29156580925282215857325937227200350542000244609280383263289720243118706105351199",
318+
"49920038676141573457451407325930326489996232208489690499754573826911037849083623\
319+
24546142615325187412887314466195222441945661833644117700809693098722026764846",
320+
];
321+
use rand::prng::XorShiftRng;
322+
seeded_value_stability::<XorShiftRng>(EXPECTED);
323+
}
184324
}

0 commit comments

Comments
 (0)