モンテカルロ統合のためのスレッドセーフ乱数生成
-
04-10-2019 - |
質問
乱数を非常に迅速に計算し、複数のスレッドに適用できるものを書き込もうとしています。私の現在のコードは次のとおりです。
/* Approximating PI using a Monte-Carlo method. */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <omp.h>
#define N 1000000000 /* As lareg as possible for increased accuracy */
double random_function(void);
int main(void)
{
int i = 0;
double X, Y;
double count_inside_temp = 0.0, count_inside = 0.0;
unsigned int th_id = omp_get_thread_num();
#pragma omp parallel private(i, X, Y) firstprivate(count_inside_temp)
{
srand(th_id);
#pragma omp for schedule(static)
for (i = 0; i <= N; i++) {
X = 2.0 * random_function() - 1.0;
Y = 2.0 * random_function() - 1.0;
if ((X * X) + (Y * Y) < 1.0) {
count_inside_temp += 1.0;
}
}
#pragma omp atomic
count_inside += count_inside_temp;
}
printf("Approximation to PI is = %.10lf\n", (count_inside * 4.0)/ N);
return 0;
}
double random_function(void)
{
return ((double) rand() / (double) RAND_MAX);
}
これは機能しますが、リソースマネージャーを観察することから、すべてのスレッドを使用していないことを知っています。 rand()はマルチスレッドコードで機能しますか?そうでない場合は、良い選択肢がありますか?どうもありがとう。ジャック
解決
は rand()
糸は安全ですか?多分そうでないかもしれません:
rand()関数は再入力する必要はありません。リエントラントである必要がない関数は、スレッドセーフである必要はありません。」
1つのテストと優れた学習演習は、コールを交換することです rand()
たとえば、固定整数で、何が起こるかを確認します。
擬似ランダム数ジェネレーターについて私が考える方法は、整数を入力としてとり、整数を出力として返すブラックボックスとしてです。特定の入力の場合、出力は常に同じですが、数値のシーケンスにパターンはなく、シーケンスは可能な出力の範囲に均一に分布しています。 (このモデルは完全に正確ではありませんが、実行します。)このブラックボックスを使用する方法は、凝視番号(シード)を選択することです。乱数ジェネレーター。 APIを設計するには、2つの一般的なアプローチがあります。
- 2つの機能、1つは初期シードを設定します(例:
srand(seed)
)そして、シーケンスから次の値を取得する1つ(例:rand()
)。 PRNGの状態は、一種のグローバル変数に内部に保存されます。新しい乱数を生成することは、スレッドセーフではありません(伝えにくいですが、出力ストリームは再現性がありません)、または多額のコードでは遅くなります(状態値に関するいくつかのシリアル化が行われます)。 - PRNG状態がアプリケーションプログラマにさらされるインターフェイス。ここでは、通常、3つの機能があります。
init_prng(seed)
, 、PRNG状態の不透明な表現を返します。get_prng(state)
, 、乱数を返し、状態変数を変更し、destroy_peng(state)
, 、割り当てられたメモリなどをクリーンアップするだけです。このタイプのAPIを使用したPRNGはすべてスレッド安全で、ロックなしで並行して実行する必要があります((現在のスレッドローカル)状態変数の管理を担当しているためです。
私は通常、Fortranで書き、使用します ラッドの Mersenne Twister Prngの実装(そのリンクは読む価値があります)。 Cには、状態を管理にさらしている適切なPRNGがたくさんあります。 prng 見栄えが良く、これを使用して(初期化と並列領域および民間の状態変数内のコールを破壊する)、まともなスピードアップが得られるはずです。
最後に、多くの場合、PRNGは、一度に一連の乱数を要求する場合にパフォーマンスを向上させることができます(たとえば、コンパイラはPRNG内部をベクトル化できます)。このため、このライブラリはしばしばそのようなものを持っています get_prng_array(state)
まるであなたが置くかのように、乱数でいっぱいの配列を返す機能 get_prng
配列要素を埋めるループで - 彼らはそれをより迅速に行います。これは2番目の最適化になります(ループのパラレル内にループに追加される必要があります。明らかに、これを行うスレッドごとのスタックスペースを使い果たしたくありません。