2013-04-10 8 views
7

C++ 11で新しい乱数ジェネレータを使用しています。さまざまな意見がありますが、このthreadから大多数がスレッドセーフではないと思われます。結果として、各スレッドが独自のRNGを使用するプログラムを作りたいと思います。私はこのプログラムのマルチスレッドとシングルスレッドのバージョンを実行して、時間を追跡するとき、彼らは、各スレッドがC++で独自のRNGを使用する方法11

#include <random> 
#include <iostream> 
#include <time.h> 
#include "omp.h" 

using namespace std; 



int main() 
{ 
    unsigned long long app = 0; 
    { 
     //mt19937_64 engine((omp_get_thread_num() + 1)); //USE FOR MULTITHREADING 
     mt19937_64 engine; //USE FOR SINGLE THREAD 
     uniform_real_distribution<double> zeroToOne(0.0, 1.0); 

     //#pragma omp parallel for reduction(+:app) //USE FOR MULTITHREADING 
     for (unsigned long long i = 0; i < 2000000000; i++) 
     { 
      if(zeroToOne(engine) < 0.5) app++; 
     } 
    } 
    cout << app << endl; 
    return 0; 
} 

例は、OpenMPのでこれを実現する方法の関連する説明に記載されています実行後に同じ時間をかけて終了します。また、appは2つのケースでは同じサイズではありませんが、異なるシードが原因であると考えられます。

質問:提供されている例では、各スレッドが独自のRNGを使用するように強制する方法を正しく示していますか?そうでない場合は、これがどのように行われたかの例を見ることができますか、またはこれを達成する方法を説明する場所への参照を取得できますか?

答えて

6

複数のスレッド間でランダムエンジンのインスタンスを共有することはできません。単一のエンジンをロックするか、スレッドごとに1つのエンジンを作成する必要があります(異なる種子を使用します(並列MTエンジンの作成に関するe4e5f4の回答に注意してください)。 OpenMPの場合、スレッドごとに1つのエンジンをベクトルに簡単に格納して、omp_get_thread_num()という結果(0〜omp_get_num_threads()–1の間にある)で取り出すことができます。

class RNG 
{ 
public: 
    typedef std::mt19937 Engine; 
    typedef std::uniform_real_distribution<double> Distribution; 

    RNG() : engines(), distribution(0.0, 1.0) 
    { 
     int threads = std::max(1, omp_get_max_threads()); 
     for(int seed = 0; seed < threads; ++seed) 
     { 
      engines.push_back(Engine(seed)); 
     } 
    } 

    double operator()() 
    { 
     int id = omp_get_thread_num(); 
     return distribution(engines[id]); 
    } 

    std::vector<Engine> engines; 
    Distribution distribution; 
}; 

int main() 
{ 
    RNG rand; 
    unsigned long app = 0; 

    #pragma omp parallel for reduction(+:app) 
    for (unsigned long long i = 0; i < 2000000000; i++) 
    { 
     if(rand() < 0.5) app++; 
    } 
} 
+0

のようないくつかの実証済みのソリューションを示唆している非常に有用である、この例をありがとうございました。私には2つの質問があります:(1)あなたは ':engines()'をなぜ組み込むのを選んだのですか?厳密に言えば、それは必要ですか? ....(2)並列化されていない私のプログラムの後のループで 'rand'オブジェクトを使用することを許可しましたか? – BillyJean

+1

@BillyJean(1)少なくとも1つが呼び出された場合、イニシャライザリストの各要素ctorを呼び出す必要はありませんが、個人的なスタイルです。 (2)100%ではないが、 'omp_get_thread_num()'は並列化されていない領域に対して0を返すと思います。 – hansmaad

+0

最終的な質問:私は 'RNG'をグローバルに、オブジェクトを' rand'グローバルにもします。条件付き '(rand()<0.5)ではなく、' rand 'に依存する計算を行うグローバル関数 'func'を呼び出します。 'func'の' rand'の使用はまだスレッドセーフであろうか?私は「はい」と言いますが、あなたの専門家の意見も聞きたいと思います。 – BillyJean

2

ランダムシードの使用はお勧めしません。それは、重複するストリームで終わる可能性があります。最終的に最終的な統計に影響を与えます。

私はthis

+2

あなたの「試してみたこのようなソリューション」は警告を出します。「注意Ⅱ:これはまだ十分にテストされていないので、多くのバグを含んでいる可能性があります。 :-) – hansmaad

+0

ランダムシーディングよりMTDCを信頼したいと思っています:) – Nishanth

+0

このノートをウェブサイトで読むと、私はちょうど微笑んだ。それにもかかわらず、この文書は非常に面白いです。初めて並列化されたPRNGを実装したとき、エンジンをシードする方法を初めて知りましたが、何の情報も見つけられませんでした。実際、単純なランダムシーディングを使用したモンテカルロシミュレーションでは、明らかな問題はありませんでした。 – hansmaad

関連する問題