Generate random numbers in advance in another thread in C ++ 11

For some numerical simulations in C ++, I need to generate a lot of random numbers with an exponential distribution (all with the same given distribution). Currently my program works well, but more than 50% of the CPU time is spent creating these random numbers.

What I would like to do is generate these random numbers in such a way as not to block the main simulation cycle. More precisely, I would like to have a stream whose task is to always keep a random number “ready in advance” and immediately generate a new one when someone reads this random number.

Does anyone know a good way to do this?

Currently, my serial code is as follows:

#include <stdio.h> #include <iostream> #include <random> using namespace std; // exponential random variable with parameter lambda class EXPGenerator{ exponential_distribution<> expo; mt19937 engine; //mersene twister public: EXPGenerator(double lambda){ expo = exponential_distribution<>(lambda); engine = mt19937(time(NULL)); } double step(){ return expo(engine); } }; int main(int argc, char *argv[]) { EXPGenerator expgen(2.0); for(int i=0; i<100000; i++) { double randv(expgen.step()); std::cout << randv << endl; // do something complicated } return 0; } 

I will compile it with clang++ -O2 --std=c++11 --stdlib=libc++ test.cpp -o test

[EDIT: Added -O2 Above]

+6
source share
5 answers

Use a limited queue and try a single thread by typing random numbers into this queue and letting this thread block the queue when the queue is full. To get a random number, pull the number from this queue and let the consumer stream block in the queue when the queue is empty.

This simple design will allow the manufacturer to produce random numbers when there is a place in the queue and available processor time.

Optimization: use a queue with lists of random numbers. In this case, the manufacturer will release a complete list with random numbers. The consumer will store a cache (possibly inside EXPGenerator) with a list from the queue. Once the cache is empty, the cache will be populated with a new list from the queue. This will reduce the overhead of the context switch and attach (for a reason) when measuring the show that it makes sense.

There should be some kind of std :: deque in the queue, where T is a random number, or std :: vector (a list of random numbers). Use the mutex to synchronize access to this std: queue and use two condition variables. One to signal that there is room for re-entering more random numbers. And one, to signal that there is already at least one element in the queue. Let the consumer wait for the second condition when the line is empty and let the producer wait for the first condition when the line is full.

+6
source

The first thing you should try is enable optimization. Try adding the -O2 option to the clang command line.

+6
source

When you work with optimization (as others suggest), you can create a bunch of random numbers in another thread that stores them in a vector, and use the message queue to transfer it to the main thread. There you can wrap it in your EXPGenerator .

+4
source

There may be an optimization that I don’t think anyone else has mentioned.

I see no reason why a consumer thread waiting for random numbers should block the wait in the producer thread. That is, if the random number cache is running dry and not blocking, just produce one or more random numbers in the consumer thread itself before checking the cache again.

Communication lockout is not required, and also facilitates the use of lightweight, loose data structures for cross-threading. Good candidates include:

In fact, if you have only one "auxiliary stream", a special transfer case between one producer and one consumer can be performed using a circular buffer without any atomic memory operations.

+2
source

OK, first create your random stream. Since thread synchronization is relatively expensive compared to generating one random, loading a vector (say with 10k power) using randoms (as suggested by Yang) is a good idea. Creating, terminating, and destroying threads is also a PITA, so the "random" loop around waiting on the "go" AutoResetEvent (see MSDN), initialized to true, the stream then generates a random vector at startup, and then is signaled when "go".

You need a mechanism for wiat until the vector is fully assembled before taking responsibility for it. You can place it in the consumer-producer queue, perhaps in the Windows message queue, as Ian suggested, but it may be easier (in this case) to simply take the vector from the stream when this is done. You can use another "full" AutoResetEvent initialized to false and wait on it for a random thread that signals this when it is done.

As soon as you take the vector, report the 'go' event to trigger a random thread that generates another vector so that it is probably already completed when you need it later.

You need a vector copy in which ownership can be easily transferred. I would probably just use a pointer, creating one with a new one in a random thread, generating randoms, copying the pointer value in the main thread and deleting it when that is done. A random thread will only be a new different vector whenever it passes "go", so it resets its own pointer. If you have a suitable smart_ptr class, you can use it, possibly unique_ptr, since it can be moved.

+1
source

Source: https://habr.com/ru/post/948039/


All Articles