2013-03-19 10 views
5

コードのOpenMP並列セクションから「バスエラー」が発生しました。私は下の私の問題の簡単なバージョンを作り直しました。このコードは基本的に、ブーストのuniform_int_distributionを使って0から20000までの整数を描画する関数uniform_distributionを多く呼び出します。Boost RandomとOpenMP

このpostは、同じオブジェクトにアクセスする2つのスレッドを警告します。私はそれが私の場合はengだと思います。 (残念ながら、私は、適切なミューテックスラッパーを書く方法を知らないので、その投稿が示唆している通りです)。

考えられる汚れた解決策は、#pragma forループ内にローカルengを作成し、これを引数としてuniform_distributionに渡すことでした。私の実際のコードでは、私は多くの関数を呼び出しているので、ローカルのengを渡すのは面倒なので、私はこの考えが気に入らない。また、engの中にuniform_distributionと宣言すれば、異なるスレッドがと同じの乱数列を生成することが懸念されます。だから私は二つの要件があります:どのようにして並列化するのですか

  1. 各スレッドは他のスレッドから確率的に独立した描画を生成していますか?
  2. RNGで競合状態が発生しませんか?

ありがとう;どんな援助も暖かく感謝しています。あなたには、いくつかのコードを並列化すると

#include <omp.h> 
#include <boost/random/uniform_int_distribution.hpp> 

boost::random::mt19937 eng; 

int uniform_distribution(int rangeLow, int rangeHigh) { 
    boost::random::uniform_int_distribution<int> unirv(rangeLow, rangeHigh); 
    return unirv(eng); 
} 
int main() 
{ 
    # pragma omp parallel for private(eng) 
    for (int bb=0; bb<10000; bb++) 
     for (int i=0; i<20000; i++) 
      int a = uniform_distribution(0,20000); 

    return 0; 
} 

答えて

3

、あなたはデータ競合を引き起こす可能性が共有リソースを、考慮する必要があり、順番に、最終的にあなたのプログラムを破損する可能性があります。 (注:すべてのデータレースでプログラムが壊れるわけではありません)engは2つ以上のスレッドで共有されていますので、正しく実行するためには避けてください。

あなたの場合の解決策は、民営化です。共有リソースのスレッドごとのコピーを作成します。 engの別のコピーを作成する必要があります。

engのための民営化を行うための方法がいくつかあります:

(1)threadprivateディレクティブ(link)を使用してください:たとえば、#pragma omp threadprivate(eng)は。ただし、このディレクティブでは、非POD構造をサポートしていないコンパイラもあります。

(2)threadprivateが利用できない場合は、engの配列を使用し、スレッドIDでアクセスします(eng[MAX_THREAD]など)。次に、スレッドID:eng[omp_get_thread()]でアクセスします。

しかし、第二の溶液は、深刻なパフォーマンスを傷つけることができ偽共有を、検討する必要があります。 eng[MAX_THREAD]の各項目が別のキャッシュライン境界に割り当てられることを保証するのが最善です。これは現代のデスクトップCPUでは通常64バイトです。誤った共有を避ける方法はいくつかあります。最も簡単な解決法は、のstructにあるengを含むパディングを使用することです。

+0

private節は、実際には変数のスレッドローカルバージョンを作成します。 threadprivateディレクティブが適用された変数とは異なり、スレッドが結合するときには割り当てられません。しかし、OPの例ではこれは問題ではありません。プライベート句がここで動作しない理由は、uniform_distributionで参照されるengがスレッド固有のものではなくグローバルなものであることです。 – jerry

+0

はい、 'priavte'に関するあなたの点は正しいです。私は文を削除しました。しかし、OPが持つ問題は本質的に民営化の問題です。スレッドローカルを作ることは解決策になります。 2番目の解決策は民営化のための手段です。 – minjang

+0

FYI:http://msdn.microsoft.com/en-us/library/c3dabskb(v=vs.80).aspx – minjang

0

あなたは2つのオプションがあります。

  • をスレッドごとに個別の乱数発生器を持っているとは異なる

まず、相互排他の例相互排他を使用してそれらをシード:

# pragma omp parallel for 
for (int bb=0; bb<10000; bb++) 
{ 
    for (int i=0; i<20000; i++) 
    { 
     // enter critical region, disallowing simulatneous access to eng 
     #pragma omp critical 
     { 
      int a = uniform_distribution(0,20000); 
     } 
     // presumably some more code... 
    } 
    // presumably some more code... 
} 

次に、シードを使用したスレッドローカルストレージの例:

# pragma omp parallel 
{ 
    // declare and seed thread-specific generator 
    boost::random::mt19937 eng(omp_get_thread_num()); 
    #pragma omp for 
    for (int bb=0; bb<10000; bb++) 
    { 
     for (int i=0; i<20000; i++) 
     { 
      int a = uniform_distribution(0,20000, eng); 
      // presumably some more code... 
     } 
     // presumably some more code... 
    } 
} 

これらのスニペットのどちらも、あなたが他の上で1つを選択することもできます(モデリング対ゲーム対関連のセキュリティを言う)あなたの要件に応じて、単なる例示です。また、使用方法に合わせて正確な実装を変更することもできます。たとえば、ジェネレータをシードする方法は、それが反復可能であるか、本当にランダムに近い(システム固有であるかどうかにかかわらず)かどうかを判断するために重要です。これは、両方のソリューションに同じように適用されます(ただし、相互排除のケースでは再現性を得るのは困難です)。

スレッドローカルジェネレータは、相互排除の場合より少ないメモリを使用する必要がありますが、高速で実行できます。

EDIT:乱数の生成は、スレッドの仕事の大部分ではない場合相互排除ソリューション、明確にするためにのみ意味があります(つまり、例の// presumably some more code...存在し、些細な量を取りませんで完了までの時間)。クリティカルセクションでは共有変数へのアクセスのみが必要です。アーキテクチャを少し変更すれば、それを細かく制御できます(スレッドローカルストレージの場合、参照を回避することもできます)

+0

クリティカルセクションを使用すると正確さが得られますが、効果的にコードをシリアライズしてもパフォーマンスは向上しません。したがって、クリティカルセクションを使用することは、このような簡単な並列化の場合には良い解決策であるとは思わない。 – minjang

+0

@ jerry:ありがとうございます。私は両方を試みた。最初のものは競合状態を解決しましたが、パフォーマンスの向上はありませんでした(ただし、テストコードが単純すぎたためかもしれません)。これはminjangが予測したものです。あなたの2番目の答えはミンジャンのものに似ていますが、それは不思議な行動をしているようです。あなたの2番目の方法を試し続けます。 – covstat

+0

@covstat、ジェリーの2番目の解決策については、 'omp for'に' private(eng) 'を入れてみてください。このコードは 'eng'を共有します。 – minjang

3

最も便利な解決策は、thread_local RNGと、各スレッドの一意の番号としてスレッドIDを含むシーディングを含むと考えます。たとえば、システム時間とスレッドIDのXORを実行してRNGをシードすることができます。 (C++ 11を使用)の線に沿って何か:

#include <omp.h> 
#include <boost/random/uniform_int_distribution.hpp> 

#include <thread> 
#include <ctime> 

boost::random::mt19937& get_rng_engine() { 
    thread_local boost::random::mt19937 eng(
    reinterpret_cast<unsigned int>(std::time(NULL))^std::this_thread::get_id()); 
    return eng; 
}; 

(注:あなたが使用しようとしている場合も<random>を使用することができますC++ 11)

あなたがCを使用できない場合++ 11の場合、代わりにboost::threadを使用して同様の動作をさせることができます。thread-local storageのBoostページも参照してください。

関連する問題