Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is mersenne twister thread safe for cpp

#include <random>

int f() {

    std::random_device seeder;
    std::mt19937 engine(seeder());
    std::uniform_int_distribution<int> dist(1, 6);

    return dist(engine);

}

Can multiple threads call this function safely? Is the function thread safe? It is reduntant to call std::random_device seeder; and std::mt19937 engine(seeder()); every time?

like image 876
cateof Avatar asked Nov 17 '16 13:11

cateof


2 Answers

No C++ std type uses global data in a non-thread-safe way. Two unrelated instances of such a type can be accessed in different threads.

By default, one instance of a type cannot be accessed from two threads without synchronization.

You are created local variables. These local variables are unrelated to any other instance of their type. There are no thread safety issues here.

Pseudo-random values are most efficiently produced by having state and reusing it. You are not doing this, so your random number from 1 to 6 will be relatively expensive to create.

std::random_device seeder;
std::mt19937 engine(seeder());
std::uniform_int_distribution<int> dist(1, 6);
return dist(engine);

Your use of std::mt19937 is redundant. You are already creating a random_device, which could be fed to dist directly, and then created an engine from it, then using the engine. The use of engine here is useless.

Traditionaly you create an engine (of some type, like mt19937) once from a seeder. You then store the engine, and repeatedly pass it to distributions.

This does the relatively expensive "real random number" generation once to generate a long series of pseudo-random numbers via engine through distribution.

Note, however, that such use has a cost; you must store the engine and you must prevent multiple-thread access to it.

The "right" way to do this is to have an object that produces random values for you, and pass it around where you need it. Storing the initial seed used would also permit you to repeat the execution of the set of random numbers involved.

If you don't like the idea of explicitly passing around your random state, you could use a thread_local (or static with a mutex guard).

thread_local std::mt19937 engine(std::random_device{}());
std::uniform_int_distribution<int> dist(1, 6);
return dist(engine);

This creates one engine per thread, and the engine is initialized with a value from your random_device.

like image 59
Yakk - Adam Nevraumont Avatar answered Nov 15 '22 19:11

Yakk - Adam Nevraumont


Can multiple threads call this function safely? Is the function thread safe?

This particular function is thread safe. It is OK to create random number generators, engines and distributions, and call generate a number in a function local engine in multiple threads.

Of course, the output from multiple threads can be interleaved since cout isn't synchronized.

Do I need to initialize the engine in every function call

That is what your function does, and while that does guarantee thread safety, it is the opposite of what you need to do. Initializing the engine every time will make the "randomness" sequence directly depend on the seeder. And it of course adds overhead to initialize the engine.

or would it be better to put the first two lines (seeder and engine) in a class constructor?

You can use a wrapper class, but you don't have to. That is orthogonal to whether you create a new engine instance in every function call. As long as each function call uses same engine as previous calls, there is no problem with randomness in that regard.

But using same engine across threads is indeed not thread safe. You could instead use one engine in each thread - or protect a shared engine with a mutex, but that has significant overhead.

like image 24
eerorika Avatar answered Nov 15 '22 20:11

eerorika