24

I have some code which looks a bit like this:

std::random_device rd;

#pragma omp parallel
{
    std::mt19937 gen(rd());
    #pragma omp for
    for(int i=0; i < N; i++)
    {
        /* Do stuff with random numbers from gen() */
    }
}

I have a few questions:

  • Is std::random_device thread safe? i.e. Is it going to do something unhelpful when several threads call it at once?
  • Is this generally a good idea? Should I be worried about overlapping random number streams?
  • Is there a better way to achieve what I want (independent random number streams in each thread - I'm not too worried about reproducibility at the moment)?

In case it make any difference to the workings of std::random_device I'm primarily running on Windows, though I would like the code to work equally well on Linux and OSX as well.

Rama
  • 3,222
  • 2
  • 11
  • 26
Theolodus
  • 2,004
  • 1
  • 23
  • 30
  • You can achieve reproducibility but using a specific seed rather than using the `std::random_device`. – Galik Feb 10 '17 at 11:15
  • `random_device` is likely to be blocking. If what you want is parallelism, there is no much point in using it like that. You could use a global PRNG seeded with `random_device` to seed the `mt19937` (but it will need explicit locking). – sbabbi Feb 10 '17 at 12:16
  • See also, https://stackoverflow.com/a/21238187/314290 – Mikhail Jun 17 '18 at 02:23

2 Answers2

5

It's not a good idea to use the random device in parallel. Even if it's blocking you may not have troubles with overlapping random number streams but you add a additional synchronization point.

You should set up as many random number engines (RNE) as many threads you want to start, omp_get_num_threads(). Create an std::vector of RNEs and seed them in the sequential part of your program. For seeding you can use the random device and a std::seed_seq.

Then use in each thread the RNE associated with with the thread number, omp_get_thread_num().

Never use the random device to generate random numbers, its's slow and in general not generating uniformly distributed random numbers!

Depending on the quality of the random numbers you need you can use one of the predefined random number generators. If you are doing Monte Carlo simulations or Cryptography be extra careful what algorithm you choose.

You'll find a lot useful information on random engines at https://en.cppreference.com/w/cpp/numeric/random.

Brolf
  • 71
  • 1
  • 4
  • You might want to actually query count of threads with `omp_get_max_threads()` instead. When ran out of parallel context, `omp_get_num_threads()` will always return 1. – val - disappointed in SE Aug 01 '20 at 18:56
1

On Windows without WinRT, it uses CryptGenRandom, which is thread-safe by https://stackoverflow.com/a/46171432/2024042

On Windows with WinRT, it uses CryptographicBuffer::GenerateRandom. There's no docs on the thread safety of this, but it appears to have no state. Hence it should be thread-safe.

On Linux, it seems to read from /dev/urandom, which is thread-safe.

I read this implementation from libs/random/src/random_device.cpp.

I have no idea what _CXXRT_STD_NAME is in that file, and googling this produces boost::random_device as the only result. Maybe it's nothing!

Kevin Yin
  • 844
  • 1
  • 8
  • 26