I have a reinforcement learning project. For this I created a vectorized environment in C++, which is a handler for multiple instances of a simple game. It is highly parallelizable. Each worker can work on their own batch of instances, there is no information exchange between them. (For the most part.)
My current approach is to use std::asynch in the following manner:
[...]
auto foo = [&](int thread_id) {
size_t chunk_size = env_count / (worker_count) + 1;
size_t my_start = thread_id * chunk_size;
size_t my_end = std::min(my_start + chunk_size, env_count);
// Take care of instances between the given interval
move_piece(my_start, my_end, ...);
raise_turns(my_start, my_end, ...);
raise_score(my_start, my_end, ...);
do_magic(my_start, my_end, ...);
return;
};
std::vector<std::future<void>> futures;
futures.reserve(worker_count);
for (auto p = 0; p < worker_count; ++p) {
futures.emplace_back(std::async(std::launch::async, foo, p));
}
// Need to wait for all to finish
for (auto &elem : futures) {
elem.wait();
}
This code runs each time the envs take an action.
I was wondering if the constant re-starting of threads (e.g. recreating the std::async
for each time the envs take action) causes significant overheads and taken a look at thread pooling.
I implemented this solution (with extra flags to know if all threads are finished) that utilizes a single job queue and multiple workers. It was slower then using std::asynch.
Is there a possibly better way to utilize concurrency? I was thinking about a one-queue-per-worker thread pool implementation, but you might be able to give me an even better approach.
Thanks in advance!
P.S.:
As an extra info, I tried to utilize data oriented design and therefore my functions share structure with the following toy example:
struct A {
int x;
int y;
};
struct B {
int z;
};
void foo(size_t my_start, size_t my_end, std::vector<A> in, std::vector<B> out){
for (size_t ii = my_start; ii < my_end; ++ii){
out[ii].z = in[ii].x + in[ii].y;
}
}