我有一个代码,我试图并行执行。
#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次。然而,我希望内循环中的每个值都有唯一的数字。
感谢。
答案 0 :(得分:1)
这里有很多问题。
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;
}