簡體   English   中英

GSL + OMP:C ++中的線程安全隨機數生成器

[英]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生成器很奇怪。 當用01播種時,這些特定的數字會生成相同的數字序列。

隱式共享變量

您的變量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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM