Как генерировать случайные числа потокобезопасным способом с помощью OpenMP
До распараллеливания я создавал один объект default_random_engine вне цикла, так как создание таких объектов не дешево. И я снова использовал его внутри петли.
При распараллеливании с OpenMP я заметил, что uniform_dist(engine) принимает изменяемую ссылку на случайный движок, который, как я предполагаю, не является потокобезопасным.
Программа не дает сбоев, но я беспокоюсь о ее корректности.
Я предполагаю, что random_device является потокобезопасным, поэтому я мог бы просто переместить определение default_random_engine внутри цикла, но я не хочу создавайте случайный объект двигателя каждую итерацию, так как я читал, что это не дешево.
default_random_engine и использовать функции OpenMP для выбора правильного объекта в начале каждой итерации на основе идентификаторов потоков.
Есть ли лучший способ?
#include <iostream>
#include <random>
using namespace std;
int main() {
    int N = 1000;
    vector<int> v(N);
    random_device r;
    default_random_engine engine(r());
    #pragma omp parallel for
    for (int i = 0; i < N; ++i) {
         uniform_int_distribution<int> uniform_dist(1, 100);
         // Perform heavy calculations
         v[i] = uniform_dist(engine); // I assume this is thread unsafe
    }
    return 0;
}
1 ответ:
Поскольку фактический код передает случайный движок многим функциям (каждая генерирует целые и вещественные числа из разных распределений), я пошел с массивом генератора на поток, потому что он налагает наименьшие изменения на кодовую базу:
Имейте в виду, что этот код предполагает, что функция#include <iostream> #include <omp.h> #include <vector> #include <random> using namespace std; int main() { random_device r; std::vector<std::default_random_engine> generators; for (int i = 0, N = omp_get_max_threads(); i < N; ++i) { generators.emplace_back(default_random_engine(r())); } int N = 1000; vector<int> v(N); #pragma omp parallel for for (int i = 0; i < N; ++i) { // Get the generator based on thread id default_random_engine& engine = generators[omp_get_thread_num()]; // Perform heavy calculations uniform_int_distribution<int> uniform_dist(1, 100); v[i] = uniform_dist(engine); // I assume this is thread unsafe } return 0; }omp_set_num_threadsникогда не вызывается в программе. Если это произойдет, то потоки смогут получить числа (omp_get_thread_num()) больше, чем старыеomp_get_max_threads(), что вызовет переполнение буфера жуки.И, к сожалению, это решение предполагает детализацию реализации, которая не требуется стандартом, как объяснено в этом другом комментарии .