6

C ++でのいくつかの数値シミュレーションでは、指数分布で多くの乱数を生成する必要があります(すべて同じ事前定義された分布で)。現在、私のプログラムは問題なく動作していますが、CPU 時間の 50% 以上がこれらの乱数の生成に費やされています。

私がやりたいことは、シミュレーションのメイン ループをブロックしない方法でこれらの乱数を生成することです。より正確には、乱数を常に「事前に準備」しておき、誰かがこの乱数を読み取ったときにすぐに新しい乱数を生成するスレッドが必要です。

これを行う良い方法を知っている人はいますか?

現在、私の順次コードは次のようになっています。

#include <stdio.h>
#include <iostream>
#include <random>

using namespace std;

// exponential random variable with parameter lambda
class EXPGenerator{
    exponential_distribution<> expo;
    mt19937 engine; //mersene twister
public:
    EXPGenerator(double lambda){
        expo = exponential_distribution<>(lambda);
        engine = mt19937(time(NULL));
    }

    double step(){  
        return expo(engine);
    }
};

int main(int argc, char *argv[])
{
    EXPGenerator expgen(2.0);
    for(int i=0; i<100000; i++) {
        double randv(expgen.step());
        std::cout << randv << endl;
        // do something complicated
    }
    return 0;
}

を使用してコンパイルしますclang++ -O2 --std=c++11 --stdlib=libc++ test.cpp -o test

[編集: 上記の -O2 を追加]

4

5 に答える 5

6

最初に試す必要があるのは、最適化を有効にすることです。-O2 オプションを clang コマンド ラインに追加してみてください。

于 2013-06-25T07:22:52.913 に答える
2

ここには、誰もまだ言及していないと思われる最適化が可能です。

乱数を待っている消費者スレッドが生産者スレッドでの待機をブロックする必要がある理由はわかりません。つまり、乱数のキャッシュが枯渇した場合は、ブロックするのではなく、キャッシュを再度チェックする前に、コンシューマ スレッド自体で 1 つ以上の乱数を生成します。

通信をブロックする必要がないため、軽量でロックのないデータ構造をスレッド間通信に使用することがはるかに簡単になります。良い候補は次のとおりです。

実際、「ヘルパー スレッド」が 1 つしかない場合、厳密に 1 つのプロデューサーと 1 つのコンシューマーの間で通信するという特殊なケースは、アトミック メモリ操作をまったく使用せずに循環バッファーを使用して実行できます。

于 2014-04-10T08:34:34.327 に答える
1

OK、まずランダム生成スレッドを作成します。スレッド同期は 1 つの乱数を生成する場合に比べて比較的コストがかかるため、(Jan が提案したように) 乱数を使用してベクトル (容量 10k など) をロードすることをお勧めします。スレッドの作成、終了、および破棄も PITA であるため、true に初期化された「go」AutoResetEvent (MSDN を参照) で待機する「ランダム」スレッドをループします。スレッドは起動時にランダムのベクトルを生成し、 「go」が合図されるたびに。

ベクトルの所有権を取得する前に、ベクトルが完全に組み立てられるまで待機するメカニズムが必要です。プロデューサー/コンシューマー キュー、Windows メッセージ キューに投稿することもできますが、Jan が示唆しているように、(この場合) 完了時にスレッドからベクターを取得する方が簡単かもしれません。false に初期化された別の「完全な」AutoResetEvent を使用して待機し、完了時にランダム スレッドがシグナルを送信することもできます。

ベクターを取得したらすぐに、「go」イベントを通知して、別のベクターを生成するランダムスレッドを開始します。これにより、後で必要になったときにすでに完了している可能性があります。

所有権を簡単に譲渡できるベクター インスタンスが必要です。私はおそらくポインターを使用し、ランダムスレッドで新しいポインターを作成し、ランダムを生成し、メインスレッドでポインター値をコピーし、完了したら削除します。ランダムスレッドは、「go」を渡すたびに別のベクトルを新しく作成するため、独自のポインターを再配置します。適切な smart_ptr クラスが利用できる場合は、それを使用できます。移動できるため、おそらく unique_ptr です。

于 2013-06-25T08:16:29.200 に答える