Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rayon and random number generator #398

Closed
rohitjoshi opened this issue Apr 14, 2018 · 6 comments
Closed

Rayon and random number generator #398

rohitjoshi opened this issue Apr 14, 2018 · 6 comments
Labels
F-new-int Functionality: new, within Rand P-low Priority: Low

Comments

@rohitjoshi
Copy link

What is the best way to pass thread-safe/thread-local random number generator so we can execute distribution operation with multiple threads?

e.g . How can I run below code to generate normal distribution in parallel using rayon or alternate way?

fn normal(mu:f64, sigma: &Vec<f64>) -> Vec<f64> {
        let mut rng = thread_rng();
        let mut v = Vec::with_capacity(sigma.len());
        for i in 0..sigma.len() {
            let mut g = Normal::new(mu, sigma[i] ).unwrap();
            v.push(g.ind_sample(&mut rng));
        }
        v
    }

I tried using below code but due to mutable rng, it is not allowed.

fn normal(mu:f64, sigma: &Vec<f64>) -> Vec<f64> {
         let mut rng = thread_rng();
        let mut v = Vec::with_capacity(sigma.len());
        (0..sigma)
            .into_par_iter()
            .map(|_| {
              let mut g = Normal::new(mu, sigma[i] ).unwrap();
               g.ind_sample(&mut rng)
             })
            .collect_into_vec(&mut v);
          v
}
@pitdicker
Copy link
Contributor

I think the easiest way would be to get your handle to ThreadRng in the loop.
Not tested, but something like this:

fn normal(mu:f64, sigma: &Vec<f64>) -> Vec<f64> {
    let mut v = Vec::with_capacity(sigma.len());
    (0..sigma)
        .into_par_iter()
        .map(|_| {
            let mut rng = thread_rng();
            let mut g = Normal::new(mu, sigma[i] ).unwrap();
            g.ind_sample(&mut rng)
        })
    .collect_into_vec(&mut v);
    v
}

You may also want to move let mut g = Normal::new(mu, sigma[i] ).unwrap() outside the loop, but it will probably not matter much.


Actually I think this is an important scenario where we don't have a good solution yet. This works with ThreadRng, but not with other RNGs that would be better suited for the task, like in this case Xoroshiro128+.

What some solutions do is generate a vector of random numbers first, and then use those in Rayon's parallel iteration. But that does not work with the way most of our Distributions work (they may use an unknown number of values from the RNG).

@pitdicker
Copy link
Contributor

pitdicker commented Apr 14, 2018

I think it would not be hard tot improve Rand for this use. We would have tot add a ParallelIterator variant of sample_iter. I'll try piece something together.

@rohitjoshi
Copy link
Author

@pitdicker Thanks. your example did work and was able to reduce the time from 4 to 2 second. let mut rng = thread_rng(); would this be created once per thread or every iteration?

@pitdicker
Copy link
Contributor

pitdicker commented Apr 15, 2018

Thanks. your example did work and was able to reduce the time from 4 to 2 second.

Nice 😄.

let mut rng = thread_rng(); would this be created once per thread or every iteration?

ThreadRng stores its internal state in thread local memory, and thread_rng() only gets a reference to that. So what this does is set op a ThreadRng for every thread rayon uses, and than for every iteration in every task look up that RNG.

I have been playing some more with rayon yesterday, and now have a commit that adds basic support in pitdicker@a216bfa. It needs some cleaning up, but is an alternative to the thread-local trickery so that it can be used with normal PRNGs.

@dhardy
Copy link
Member

dhardy commented Apr 15, 2018

Great addition @pitdicker!

I think for now you might just as well do g.ind_sample(&mut thread_rng()) @rohitjoshi.

@dhardy dhardy added X-enhancement F-new-int Functionality: new, within Rand P-low Priority: Low labels Apr 15, 2018
@dhardy
Copy link
Member

dhardy commented Apr 16, 2018

#399 is open; I think any further discussion can take place there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
F-new-int Functionality: new, within Rand P-low Priority: Low
Projects
None yet
Development

No branches or pull requests

3 participants