Skip to content

Commit

Permalink
Merge #53
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
bors[bot] and cuviper committed Jul 5, 2018
2 parents 8916248 + 8b5a092 commit 86e019b
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 8 deletions.
46 changes: 46 additions & 0 deletions benches/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,52 @@ fn from_str_radix_36(b: &mut Bencher) {
from_str_radix_bench(b, 36);
}

fn rand_bench(b: &mut Bencher, bits: usize) {
let mut rng = get_rng();

b.iter(|| rng.gen_bigint(bits));
}

#[bench]
fn rand_64(b: &mut Bencher) {
rand_bench(b, 1 << 6);
}

#[bench]
fn rand_256(b: &mut Bencher) {
rand_bench(b, 1 << 8);
}

#[bench]
fn rand_1009(b: &mut Bencher) {
rand_bench(b, 1009);
}

#[bench]
fn rand_2048(b: &mut Bencher) {
rand_bench(b, 1 << 11);
}

#[bench]
fn rand_4096(b: &mut Bencher) {
rand_bench(b, 1 << 12);
}

#[bench]
fn rand_8192(b: &mut Bencher) {
rand_bench(b, 1 << 13);
}

#[bench]
fn rand_65536(b: &mut Bencher) {
rand_bench(b, 1 << 16);
}

#[bench]
fn rand_131072(b: &mut Bencher) {
rand_bench(b, 1 << 17);
}

#[bench]
fn shl(b: &mut Bencher) {
let n = BigUint::one() << 1000;
Expand Down
15 changes: 9 additions & 6 deletions src/bigrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use rand::prelude::*;
use rand::distributions::uniform::{SampleUniform, UniformSampler};
use rand::AsByteSliceMut;

use BigInt;
use BigUint;
Expand Down Expand Up @@ -39,13 +40,15 @@ impl<R: Rng + ?Sized> RandBigInt for R {
fn gen_biguint(&mut self, bit_size: usize) -> BigUint {
use super::big_digit::BITS;
let (digits, rem) = bit_size.div_rem(&BITS);
let mut data = Vec::with_capacity(digits + 1);
for _ in 0..digits {
data.push(self.gen());
}
let mut data = vec![BigDigit::default(); digits + (rem > 0) as usize];
// `fill_bytes` is faster than many `gen::<u32>` calls
self.fill_bytes(data[..].as_byte_slice_mut());
// Swap bytes per the `Rng::fill` source. This might be
// unnecessary if reproducibility across architectures is not
// desired.
data.to_le();
if rem > 0 {
let final_digit: BigDigit = self.gen();
data.push(final_digit >> (BITS - rem));
data[digits] >>= BITS - rem;
}
BigUint::new(data)
}
Expand Down
144 changes: 142 additions & 2 deletions tests/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod biguint {
use num_bigint::{BigUint, RandBigInt, RandomBits};
use num_traits::Zero;
use rand::thread_rng;
use rand::Rng;
use rand::{Rng, SeedableRng};
use rand::distributions::Uniform;

#[test]
Expand Down Expand Up @@ -89,13 +89,83 @@ mod biguint {
assert!(n < u);
}
}

fn seeded_value_stability<R: SeedableRng + RandBigInt>(expected: &[&str]) {
let mut seed = <R::Seed>::default();
for (i, x) in seed.as_mut().iter_mut().enumerate() {
*x = (i as u8).wrapping_mul(191);
}
let mut rng = R::from_seed(seed);
for (i, &s) in expected.iter().enumerate() {
let n: BigUint = s.parse().unwrap();
let r = rng.gen_biguint((1 << i) + i);
assert_eq!(n, r);
}
}

#[test]
fn test_chacha_value_stability() {
const EXPECTED: &[&str] = &[
"0",
"0",
"52",
"84",
"23780",
"86502865016",
"187057847319509867386",
"34045731223080904464438757488196244981910",
"23813754422987836414755953516143692594193066497413249270287126597896871975915808",
"57401636903146945411652549098818446911814352529449356393690984105383482703074355\
67088360974672291353736011718191813678720755501317478656550386324355699624671",
];
use rand::prng::ChaChaRng;
seeded_value_stability::<ChaChaRng>(EXPECTED);
}

#[test]
fn test_isaac_value_stability() {
const EXPECTED: &[&str] = &[
"1",
"4",
"3",
"649",
"89116",
"7730042024",
"20773149082453254949",
"35999009049239918667571895439206839620281",
"10191757312714088681302309313551624007714035309632506837271600807524767413673006",
"37805949268912387809989378008822038725134260145886913321084097194957861133272558\
43458183365174899239251448892645546322463253898288141861183340823194379722556",
];
use rand::prng::IsaacRng;
seeded_value_stability::<IsaacRng>(EXPECTED);
}

#[test]
fn test_xorshift_value_stability() {
const EXPECTED: &[&str] = &[
"1",
"0",
"37",
"395",
"181116",
"122718231117",
"1068467172329355695001",
"28246925743544411614293300167064395633287",
"12750053187017853048648861493745244146555950255549630854523304068318587267293038",
"53041498719137109355568081064978196049094604705283682101683207799515709404788873\
53417136457745727045473194367732849819278740266658219147356315674940229288531",
];
use rand::prng::XorShiftRng;
seeded_value_stability::<XorShiftRng>(EXPECTED);
}
}

mod bigint {
use num_bigint::{BigInt, RandBigInt, RandomBits};
use num_traits::Zero;
use rand::thread_rng;
use rand::Rng;
use rand::{Rng, SeedableRng};
use rand::distributions::Uniform;

#[test]
Expand Down Expand Up @@ -181,4 +251,74 @@ mod bigint {
check(-l.clone(), u.clone());
check(-u.clone(), -l.clone());
}

fn seeded_value_stability<R: SeedableRng + RandBigInt>(expected: &[&str]) {
let mut seed = <R::Seed>::default();
for (i, x) in seed.as_mut().iter_mut().enumerate() {
*x = (i as u8).wrapping_mul(191);
}
let mut rng = R::from_seed(seed);
for (i, &s) in expected.iter().enumerate() {
let n: BigInt = s.parse().unwrap();
let r = rng.gen_bigint((1 << i) + i);
assert_eq!(n, r);
}
}

#[test]
fn test_chacha_value_stability() {
const EXPECTED: &[&str] = &[
"0",
"-6",
"-1",
"1321",
"-147247",
"8486373526",
"-272736656290199720696",
"2731152629387534140535423510744221288522",
"-28820024790651190394679732038637785320661450462089347915910979466834461433196572",
"501454570554170484799723603981439288209930393334472085317977614690773821680884844\
8530978478667288338327570972869032358120588620346111979053742269317702532328",
];
use rand::prng::ChaChaRng;
seeded_value_stability::<ChaChaRng>(EXPECTED);
}

#[test]
fn test_isaac_value_stability() {
const EXPECTED: &[&str] = &[
"1",
"0",
"5",
"113",
"-132240",
"-36348760761",
"-365690596708430705434",
"-14090753008246284277803606722552430292432",
"-26313941628626248579319341019368550803676255307056857978955881718727601479436059",
"-14563174552421101848999036239003801073335703811160945137332228646111920972691151\
88341090358094331641182310792892459091016794928947242043358702692294695845817",
];
use rand::prng::IsaacRng;
seeded_value_stability::<IsaacRng>(EXPECTED);
}

#[test]
fn test_xorshift_value_stability() {
const EXPECTED: &[&str] = &[
"-1",
"-4",
"11",
"-1802",
"966495",
"-62592045703",
"-602281783447192077116",
"-34335811410223060575607987996861632509125",
"29156580925282215857325937227200350542000244609280383263289720243118706105351199",
"49920038676141573457451407325930326489996232208489690499754573826911037849083623\
24546142615325187412887314466195222441945661833644117700809693098722026764846",
];
use rand::prng::XorShiftRng;
seeded_value_stability::<XorShiftRng>(EXPECTED);
}
}

0 comments on commit 86e019b

Please sign in to comment.