5

コードの OpenMP 並列セクションから「バス エラー」が発生しています。以下の問題の簡単なバージョンを再作成しました。このコードは基本的に、uniform_distributionBoost の uniform_int_distribution を使用して 0 から 20000 までの整数を描画する関数 を多数呼び出します。

この投稿は、2 つのスレッドが同じオブジェクトにアクセスしていることを警告しています。私のeng場合だと思います。(残念ながら、その投稿が示唆するように、「適切なミューテックスラッパー」の書き方がわかりません)。

私が考えた汚い解決策はeng、ループ内にローカルを作成し、#pragma forそれを引数として に渡すことでしたuniform_distribution。私の実際のコードでは、多くの関数を呼び出しており、ローカルを渡すのengは面倒なので、このアイデアは好きではありません。また、私の懸念は、内部で宣言すると、異なるスレッドが同じ乱数シーケンスを生成することです。したがって、2 つの要件があります。enguniform_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;
}
4

3 に答える 3

3

最も便利な解決策は、thread_localRNGと、各スレッドの一意の番号としてスレッド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のBoostページも参照してください。

于 2013-03-19T17:26:01.367 に答える
3

一部のコードを並列化するときは、共有リソースを考慮する必要があります。これにより、データ競合が発生し、最終的にプログラムが破損する可能性があります。(注: すべてのデータ競合がプログラムを破壊するわけではありません。)

あなたの場合、正しく予想したようengに、2 つ以上のスレッドで共有されます。これは、正しく実行するために回避する必要があります。

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

の民営化を行う方法はいくつかありますeng

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

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

ただし、2 番目の解決策では、パフォーマンスに深刻な影響を与える可能性のある偽の共有を考慮する必要があります。の各項目がeng[MAX_THREAD]個別のキャッシュ ライン境界に割り当てられることを保証するのが最善です。これは、最新のデスクトップ CPU では通常 64 バイトです。偽の共有を回避する方法もいくつかあります。最も簡単な解決策は、パディングを使用することです。たとえば、保持char padding[x]する aで。structeng

于 2013-03-19T17:21:19.230 に答える
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...
    }
}

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

スレッドローカルジェネレーターはより高速に実行される可能性がありますが、相互排除の場合はメモリの使用量が少なくなるはずです。

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

于 2013-03-19T17:23:52.257 に答える