Rust: ThreadRng performance bug

Created on 8 Jul 2017  ·  3Comments  ·  Source: rust-lang/rust

ThreadRng works 5 times slower than ThreadLocalRandom in Java.

I run this benchmark in Rust:

#[bench]
fn bench_rnd(b: &mut Bencher) {
    b.iter(|| rand::thread_rng().gen_range::<f64>(2.0, 100.0));
}

On my laptop the result is:
test tests::bench_rnd ... bench: 49 ns/iter (+/- 1)

But if I run the same benchmark on JHM:

@Benchmark
public double testRnd() {
    return ThreadLocalRandom.current().nextDouble(2, 100);
}

On my laptop the result is:
Benchmark Mode Cnt Score Error Units
Main.testRnd avgt 20 9,018 ± 0,094 ns/op

So the difference is 5.44 times looks like performance bug.

Meta

Rust:

rbose
rustc 1.20.0-nightly (c9bb93576 2017-06-24)
binary: rustc
commit-hash: c9bb93576d4484edd1b3c40eb2aea0dfa0788851
commit-date: 2017-06-24
host: x86_64-unknown-linux-gnu
release: 1.20.0-nightly
LLVM version: 4.0

Java:
OpenJDK 1.8.131

Most helpful comment

Since rand is an external crate with its own repository, this issue probably belongs there, unless you're able to somehow demonstrate that this is Rust's fault, and not rand's fault.

However, the question you should really be asking, is what kind of random numbers are you getting? rand::ThreadRng is a cryptographically secure RNG, so it's going to be at an inherent performance disadvantage to RNGs that don't have to be as secure. Looking at the documentation... "Instances of ThreadLocalRandom are not cryptographically secure. " which means you're not even doing a fair comparison.

Running your benchmark as written is 21ns/iter for me.

Modifying your benchmark to cache the result of thread_rng() speeds it up 15 ns/iter for me.

#[bench]
fn bench_rnd(b: &mut Bencher) {
    let mut rng = rand::thread_rng();
    b.iter(|| rng.gen_range::<f64>(2.0, 100.0));
}

If I use weak_rng() however and throw out cryptographic security, it races ahead to 3 ns/iter.

#[bench]
fn bench_rnd(b: &mut Bencher) {
    let mut rng = rand::weak_rng();
    b.iter(|| rng.gen_range::<f64>(2.0, 100.0));
}

All 3 comments

Since rand is an external crate with its own repository, this issue probably belongs there, unless you're able to somehow demonstrate that this is Rust's fault, and not rand's fault.

However, the question you should really be asking, is what kind of random numbers are you getting? rand::ThreadRng is a cryptographically secure RNG, so it's going to be at an inherent performance disadvantage to RNGs that don't have to be as secure. Looking at the documentation... "Instances of ThreadLocalRandom are not cryptographically secure. " which means you're not even doing a fair comparison.

Running your benchmark as written is 21ns/iter for me.

Modifying your benchmark to cache the result of thread_rng() speeds it up 15 ns/iter for me.

#[bench]
fn bench_rnd(b: &mut Bencher) {
    let mut rng = rand::thread_rng();
    b.iter(|| rng.gen_range::<f64>(2.0, 100.0));
}

If I use weak_rng() however and throw out cryptographic security, it races ahead to 3 ns/iter.

#[bench]
fn bench_rnd(b: &mut Bencher) {
    let mut rng = rand::weak_rng();
    b.iter(|| rng.gen_range::<f64>(2.0, 100.0));
}

Also note that Java's ThreadLocalRandom is an extremely simple linear congruential PRNG, while Rust's rand's thread_rng() is based on the ISAAC (claimed-to-be) cryptographically secure PRNG which is more expensive, so this benchmark is not an exactly apple-to-apple comparison.

Thank you a lot for the explanation.

Was this page helpful?
0 / 5 - 0 ratings