問題
約 100 万個の 32 ビット疑似乱数に基づいて数値シミュレーション (暗号化ではない) を行う Linux 用の C++11 アプリケーションを作成するつもりです。高速化するために、デスクトップ CPU のすべてのコアを使用して並列スレッドでシミュレーションを実行したいと考えています。ブーストによって提供されるMersenne Twister を PRNG として使用したいと思いmt19937
ます。パフォーマンス上の理由から、スレッドごとに 1 つの PRNG を使用する必要があると思います。複数のスレッドで乱数の同じサブシーケンスを生成しないようにするために、それらをシードする方法がわかりません。
代替案
これまでに考えた代替案は次のとおりです。
から独立してすべてのスレッドの PRNG をシードし
/dev/urandom
ます。システム内部の PRNG がどのように動作するのかわからないため、システムのエントロピー プールが枯渇した場合が少し心配です。
/dev/urandom
Mersenne Twister 自体を使用しているため、Mersenne Twister の連続した状態を正確に識別する連続したシードを誤って取得することはありますか? おそらく、次のポイントに対する私の懸念と強く関連しています。1 つの PRNG をシード
/dev/urandom
し、最初のものから他をシードします。基本的に同じ懸念事項: ある PRNG を使用して、同じアルゴリズムを使用する別の PRNG をシードすることは良いことですか、悪いことですか? または、言い換えると、 から 625 個の 32 ビット整数を読み取ることは、この生成中の任意の時点で
mt19937
のジェネレーターの内部状態に直接対応しますか?mt19937
メルセンヌ以外の情報を最初からシードします。
乱数の生成と初期シードの生成に同じアルゴリズムを使用するのは、どこか悪い考えに思えるので、Mersenne Twister アルゴリズムに依存しない要素を導入することを考えました。たとえば、スレッド ID を初期シード ベクトルの各要素に XOR することができます。それは物事をより良くしますか?
スレッド間で 1 つの PRNG を共有します。
これにより、メルセンヌ ツイスターの既知の望ましい特性をすべて備えたシーケンスが 1 つだけ存在することが保証されます。しかし、そのジェネレーターへのアクセスを制御するために必要なロックのオーバーヘッドは、やや心配です。反対の証拠が見つからなかったので、ライブラリ ユーザーとして、PRNG への同時アクセスを防止する責任があると思います。
すべての乱数を事前に生成します。
これにより、1 つのスレッドが必要な 1M 乱数をすべて前もって生成し、後で別のスレッドで使用できるようになります。4M のメモリ要件は、アプリケーション全体のメモリ要件に比べて小さくなります。このアプローチで私が最も心配しているのは、乱数の生成自体が並行していないことです。このアプローチ全体も、あまりうまくスケーリングしません。
質問
これらのアプローチのどれを提案しますか?またその理由は? それとも別の提案がありますか?
私の懸念のどれが正当で、どれが単に物事が実際にどのように機能するかについての洞察の欠如によるものか知っていますか?