[英]GSL+OMP: Thread safe random number generators in C++
我有一個試圖在其中並行執行的代碼。
#include<iostream>
#include<omp.h>
#include<math.h>
#include<cstdlib>
#include<iterator>
#include<string.h>
#include<vector>
#include<map>
#include<time.h>
#include<gsl/gsl_rng.h>
#include<gsl/gsl_randist.h>
gsl_rng ** threadvec = new gsl_rng*[omp_get_num_threads()];
using namespace std;
int main(){
clock_t begin = omp_get_wtime();
vector<double> PopVals;
map<int, vector<double> > BigMap;
int Num1 = 100;
double randval;
int Num2 = 10;
#pragma omp parallel
{
gsl_rng_env_setup();
for (int b = 0; b < omp_get_num_threads(); b++)
threadvec[b] = gsl_rng_alloc(gsl_rng_taus);
}
for( int i = 0; i < Num1; i++){
PopVals.resize(Num2);
#pragma omp parallel for
for( int j = 0; j < Num2; j++){
randval = gsl_rng_uniform(threadvec[omp_get_thread_num()]);
PopVals[j] = randval;
}
BigMap.insert(make_pair(i,PopVals));
PopVals.clear();
}
map<int,vector<double> >::iterator it = BigMap.find(Num1-1);
vector<double> OutVals = it->second;
for (int i = 0; i < Num2; i++)
cout << endl << OutVals[i] << endl;
for (int b = 0; b < omp_get_num_threads(); b++)
gsl_rng_free(threadvec[b]);
clock_t end = omp_get_wtime();
double elapsed_time = double(end - begin);
cout << endl << "Time taken to run: " << elapsed_time << " secs" << endl;
}
當我運行此命令時,有8個線程並行執行嵌套循環,但是我仍然看到每個線程的隨機數相同。 我將此行為歸因於每次迭代均未設置種子。 如果有人可以指出,我將如何以線程安全的方式在循環的每次迭代中生成唯一的隨機數,這將是很棒的。
上面的代碼的輸出是0.793816,是10倍。 而我想要內部循環中每個值的唯一數字。
謝謝。
這里有多個問題。
omp_get_num_threads
在並行區域之外, omp_get_num_threads()
始終返回1
。 使用omp_get_max_threads()
代替,它將返回任何即將到來的parallel
區域的線程數,除非手動覆蓋。 特別是threadvec
只有一個條目。
在並行區域中調用gsl_rng_env_setup
無法正常工作。 另外,您正在嘗試通過所有線程分配rng的整個向量...只需刪除並行區域並正確使用omp_get_max_threads()
。 或者,您也可以這樣做:
gsl_rng_env_setup(); // serial
#pragma omp parallel
threadvec[omp_get_thread_num()] = gsl_rng_alloc(gsl_rng_taus);
盡管從文檔中還不清楚它是否是線程安全的,但仍不能100%清除,因此只需使用串行循環版本即可。
默認情況下,所有rng的種子編號都相同,因此顯然它們將返回完全相同的序列。 用線程號正確播種它們,例如gsl_rng_set(threadvec[b], b * 101);
。 請注意,Tausworthe生成器很奇怪。 當用0
或1
播種時,這些特定的數字會生成相同的數字序列。
您的變量randval
在並行區域之外定義,因此它是隱式共享的。 您可以強制將其設為私有,但是最好在本地聲明變量。 這使得有關OpenMP代碼的推理更加容易。
最后,它看起來像這樣:
#include <cstdlib>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>
#include <iostream>
#include <iterator>
#include <map>
#include <math.h>
#include <omp.h>
#include <string.h>
#include <time.h>
#include <vector>
// DO NOT using namespace std;
int main() {
clock_t begin = omp_get_wtime();
std::vector<double> PopVals;
std::map<int, std::vector<double>> BigMap;
constexpr int Num1 = 100;
constexpr int Num2 = 10;
gsl_rng_env_setup();
gsl_rng **threadvec = new gsl_rng *[omp_get_max_threads()];
for (int b = 0; b < omp_get_max_threads(); b++) {
threadvec[b] = gsl_rng_alloc(gsl_rng_taus);
gsl_rng_set(threadvec[b], b * 101);
}
for (int i = 0; i < Num1; i++) {
PopVals.resize(Num2);
#pragma omp parallel for
for (int j = 0; j < Num2; j++) {
double randval = gsl_rng_uniform(threadvec[omp_get_thread_num()]);
PopVals[j] = randval;
}
BigMap.insert(std::make_pair(i, PopVals));
PopVals.clear();
}
std::map<int, std::vector<double>>::iterator it = BigMap.find(Num1 - 1);
std::vector<double> OutVals = it->second;
for (int i = 0; i < Num2; i++)
std::cout << std::endl << OutVals[i] << std::endl;
for (int b = 0; b < omp_get_max_threads(); b++)
gsl_rng_free(threadvec[b]);
clock_t end = omp_get_wtime();
double elapsed_time = double(end - begin);
std::cout << std::endl << "Time taken to run: " << elapsed_time << " secs" << std::endl;
delete[] threadvec;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.