There seem to be a couple problems getting conflated here.
First, the non-thread-safe nature of rand()
function means that calling rand()
at the same time from different threads might yield different values than if it were called sequentially. It's probably easiest to explain this with a simple example, so let's look at a 32-bit version of PCG since it's nice and simple. It has a 32-bit state, and generates 32-bit numbers like this:
static uint32_t state = ...;
static uint32_t generate(void) {
uint32_t s = state;
uint32_t v = ((s >> ((s >> 28) + 4)) ^ s) * (277803737U);
v ^= v >> 22;
state = state * 747796405U + 1729U;
return v;
}
Now think about what happens if two threads call generate()
at roughly the same time. Maybe they both get the same value for state
, and so generate the same random number twice. Maybe one updates state
before the other reads it, so they get different values.
We can eliminate the problem by protecting the generate()
function with a mutex or, in the case of 32-bit PGC (and this is why I use it for reproducible numbers), using atomics. If we do that then we'll always get the same numbers in the same order.
Part two of the problem is what happens when the order of the callers gets mixed up in your code. Let's say you have two threads (called A and B), and they each have to run two iterations of your loop. Even if you're getting your random numbers from a thread-safe source, the order of the calls could be AABB, ABAB, ABBA, BBAA, BABA, or BAAB, each of which would cause your code to generate a different result.
There are several straightforward ways around this. First, you could use synchronization primitives to ensure that each iteration calls generate
in the order you want. The easiest way would probably be to use a queue, but you'll waste a lot of time synchronizing and you'll lose some opportunities for parallelism (and you have to restructure your code significantly).
If you have a relatively small number of iterations, you might consider generating an array ahead of time. Think:
int i;
int nums[LEN];
for (i = 0 ; i < LEN ; i++)
nums[i] = generate();
#pragma omp parallel for ...
for (i = 0 ; i < LEN ; i++) {
do_stuff(nums[i]);
}
A better solution, though, might be to just ditch the idea of generating random numbers altogether and instead use hashing. https://burtleburtle.net/bob/hash/integer.html has some options. An example of that would be something like
for (int i = 0 ; i < LEN ; i++) {
do_stuff(hash(i));
}
Of course you can throw in some salt, maybe even use rand()
to generate your salt.