Skip to content

Commit 330efe9

Browse files
authored
Deterministic Rayon monte carlo example (#1236)
* Deterministic Rayon monte carlo * Update deterministic mt with a batching example * discuss determinism in the context of rayon + rand * reword the discussion Co-authored-by: Mason Kramer <[email protected]>
1 parent 3543f4b commit 330efe9

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,4 @@ libc = { version = "0.2.22", optional = true, default-features = false }
8383
rand_pcg = { path = "rand_pcg", version = "0.3.0" }
8484
# Only to test serde1
8585
bincode = "1.2.1"
86+
rayon = "1.5.3"

examples/rayon-monte-carlo.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2018 Developers of the Rand project.
2+
// Copyright 2013-2018 The Rust Project Developers.
3+
//
4+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5+
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7+
// option. This file may not be copied, modified, or distributed
8+
// except according to those terms.
9+
10+
//! # Monte Carlo estimation of π with a chosen seed and rayon for parallelism
11+
//!
12+
//! Imagine that we have a square with sides of length 2 and a unit circle
13+
//! (radius = 1), both centered at the origin. The areas are:
14+
//!
15+
//! ```text
16+
//! area of circle = πr² = π * r * r = π
17+
//! area of square = 2² = 4
18+
//! ```
19+
//!
20+
//! The circle is entirely within the square, so if we sample many points
21+
//! randomly from the square, roughly π / 4 of them should be inside the circle.
22+
//!
23+
//! We can use the above fact to estimate the value of π: pick many points in
24+
//! the square at random, calculate the fraction that fall within the circle,
25+
//! and multiply this fraction by 4.
26+
//!
27+
//! Note on determinism:
28+
//! It's slightly tricky to build a parallel simulation using Rayon
29+
//! which is both efficient *and* reproducible.
30+
//!
31+
//! Rayon's ParallelIterator api does not guarantee that the work will be
32+
//! batched into identical batches on every run, so we can't simply use
33+
//! map_init to construct one RNG per Rayon batch.
34+
//!
35+
//! Instead, we do our own batching, so that a Rayon work item becomes a
36+
//! batch. Then we can fix our rng stream to the batched work item.
37+
//! Batching amortizes the cost of constructing the Rng from a fixed seed
38+
//! over BATCH_SIZE trials. Manually batching also turns out to be faster
39+
//! for the nondeterministic version of this program as well.
40+
41+
#![cfg(all(feature = "std", feature = "std_rng"))]
42+
43+
use rand::distributions::{Distribution, Uniform};
44+
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
45+
use rayon::prelude::*;
46+
47+
static SEED: u64 = 0;
48+
static BATCH_SIZE: u64 = 10_000;
49+
static BATCHES: u64 = 1000;
50+
51+
fn main() {
52+
let range = Uniform::new(-1.0f64, 1.0);
53+
54+
let in_circle = (0..BATCHES)
55+
.into_par_iter()
56+
.map(|i| {
57+
let mut rng = ChaCha8Rng::seed_from_u64(SEED);
58+
// We chose ChaCha because it's fast, has suitable statical properties for simulation,
59+
// and because it supports this set_stream() api, which lets us chose a different stream
60+
// per work item. ChaCha supports 2^64 independent streams.
61+
rng.set_stream(i);
62+
let mut count = 0;
63+
for _ in 0..BATCH_SIZE {
64+
let a = range.sample(&mut rng);
65+
let b = range.sample(&mut rng);
66+
if a * a + b * b <= 1.0 {
67+
count += 1;
68+
}
69+
}
70+
count
71+
})
72+
.reduce(|| 0usize, |a, b| a + b);
73+
74+
// prints something close to 3.14159...
75+
println!(
76+
"π is approximately {}",
77+
4. * (in_circle as f64) / ((BATCH_SIZE * BATCHES) as f64)
78+
);
79+
}

0 commit comments

Comments
 (0)