2016-11-16 3 views
0

は、私は、スレッドセーフ機能スレッドセーフな並列RNG()

rand_r 

で円周率の計算のこのバージョンを使用しかし、これを実行しているときに、遅くなることが表示されます(と答えが間違っています)プログラムは、スレッドセーフではない

rand() 

を使用するシーケンシャルプログラムと比較して並列に実行されます。この使用方法はスレッドセーフではないようです。しかし、なぜ私はスレッドセーフなPRNGについての多くの質問を読んでいて、rand_rは十分安全であるべきだということを知っていたので、なぜわかりません。

#include <iostream> 
#include <random> 
#include <ctime> 
#include "omp.h" 
#include <stdlib.h> 

using namespace std; 

unsigned seed; 

int main() 
{ 
double start = time(0); 

int i, n, N; 
double x, y; 



N = 1<<30; 
n = 0; 

double pi; 



#pragma omp threadprivate(seed) 
#pragma omp parallel private(x, y) reduction(+:n) 
{ 

for (i = 0; i < N; i++) { 
seed = 25234 + 17 * omp_get_thread_num(); 
    x = rand_r(&seed)/(double) RAND_MAX; 
    y = rand_r(&seed)/(double) RAND_MAX; 

    if (x*x + y*y <= 1) 
     n++; 
} 
} 

pi = 4. * n/(double) (N); 

cout << pi << endl; 

double stop = time(0); 

cout << (stop - start) << endl; 

return 0; 
} 

P.S.ちなみに、魔法の数字は何ですか?

seed = 25234 + 17 * omp_get_thread_num(); 

?私はいくつかの答えからそれらを盗んだ。

EDIT: Gillesのコメントが私を助けました。解決方法は次のとおりです。 1. forループとシードの初期化を切り替えるには 2.

変更されたコードのための#pragma OMPを追加するには、問題が解決され

#pragma omp parallel private(x, y, seed) 
{ 
seed = 25234 + 17 * omp_get_thread_num(); 

#pragma omp for reduction(+:n) 
for (int i = 0; i < N; i++) { 

    x = (double) rand_r(&seed)/(double) RAND_MAX; 
    y = (double) rand_r(&seed)/(double) RAND_MAX; 

    if (x*x + y*y <= 1) 
     n++; 
} 

} 

を読み込みます。

+0

答えが間違っているとはどういう意味ですか? rand_rがスレッドごとに別々のシーケンスをサポートすることを期待していますか?確かに、これらのシーケンスの1つ以上は、同じジェネレータの単一のスレッド呼び出しと一致しません。 2つのスレッドが同じシーケンスに入った場合、真のPRNGは「間違っている」と考えられますが、非並列ジェネレータを使用してショートカットを作成すると、そのような保証はありません。 – tim18

+0

@ tim18 "答えが間違っている"とは、Piの結果値が3.14ではない(ただし、rand()で逐次プログラムを使用する場合は3.14)。はい、スレッドの数に応じてシードを使用しているので、スレッドごとに別々のシーケンスが必要です。または、私は何かを逃していますか?... – newt

+0

はい、あなたは何かが間違っています:ループの各反復でシードを初期化してはいけません。 ATMのやり方で、まったく同じ「ランダム」番号を繰り返し生成します。 – Gilles

答えて

0

明らかに、rand_r()にはrand()と比較して多くの命令があります。以下は、1つの実装からコピーされたものです。だから、rand_r()がrand()より1回のラウンドを完了するのに時間がかかります。

int 
rand_r(unsigned int *ctx) 
{ 
    u_long val = (u_long) *ctx; 
    int r = do_rand(&val); 

    *ctx = (unsigned int) val; 
    return (r); 
} 


static u_long next = 1; 

int 
rand() 
{ 
    return (do_rand(&next)); 
} 

rand()はスレッドセーフではないため、rand()を並列に使用すると、出力が正しくない可能性があります。悪い点は、結果がまだ得られ、小規模テストで正しいかどうかわからないことです。

+0

です。並列乱数生成のためにいくつかのライブラリを使って乱数生成の利点をいくつか得ることができるのでしょうか?それでは、私と同じ質問を見て、rand_rを使ってパフォーマンスを改善することを勧めました。そして、π評価の時間は実際に逐次プログラムと比較して減少した。だからこそ、私の場合、時間がかえって増えてしまうのは奇妙なことです。 それでも、rand_r()の結果が正しくない(piは3.14と等しくない)理由は分かりませんが、rand()を順番に使用している間は正しいです。 – newt

関連する問題