我需要一些帮助来将pi计算与monmp carlo方法并行化,使用给定的随机数生成器进行openmp,这不是线程安全的。
首先:This SO线程没有帮助我。
我自己尝试的是以下#pragma omp语句。我认为i,x和y变量应该由每个线程初始化,而不应该是私有的。 z是圆圈中所有命中的总和,因此它应该在for循环后隐含的barriere之后求和。
认为主要问题是随机数发生器的静态状态变量。我创建了一个调用函数的关键部分,因此每次只有一个线程可以执行它。但Pi解决方案不能随着更高的值而扩展。
注意:我不应该使用其他RNG,但可以对其进行一些修改。
int main (int argc, char *argv[]) {
int i, z = 0, threads = 8, iters = 100000;
double x,y, pi;
#pragma omp parallel firstprivate(i,x,y) reduction(+:z) num_threads(threads)
for (i=0; i<iters; ++i) {
#pragma omp critical
{
x = rng_doub(1.0);
y = rng_doub(1.0);
}
if ((x*x+y*y) <= 1.0)
z++;
}
pi = ((double) z / (double) (iters*threads))*4.0;
printf("Pi: %lf\n", pi);;
return 0;
}
这个RNG实际上是一个包含文件,但由于我不确定我是否正确创建了头文件,我将其集成到另一个程序文件中,所以我只有一个.c文件。
#define RNG_MOD 741025
int rng_int(void) {
static int state = 0;
return (state = (1366 * state + 150889) % RNG_MOD);
}
double rng_doub(double range) {
return ((double) rng_int()) / (double) ((RNG_MOD - 1)/range);
}
我也尝试将静态int状态设为全局,但它不会改变我的结果,也许我做错了。那么请你能帮我做出正确的改变吗?非常感谢你!
答案 0 :(得分:8)
原始线性全等PRNG的周期长度为49400,因此您只获得29700个独特测试点。这是一个可怕的发生器,可用于任何类型的蒙特卡罗模拟。即使您进行了100000000次试验,也不会接近Pi的真实值,因为您只是一遍又一遍地重复相同的点,因此z
的最终值和{{1简单地乘以相同的常数,在分割期间最终取消。
Z boson引入的每线程种子通过随着OpenMP线程总数增加的唯一点数增加了一点点情况。增加不是线性的,因为如果一个PRNG的种子落入另一个PRNG的序列中,则两个PRNG产生相同的序列,其移位不超过49400个元素。给定周期长度,每个PRNG覆盖49400 / RNG_MOD =总输出范围的6.7%,这是两个PRNG同步的概率。总共有RNG_MOD / 49400 = 15个唯一序列。它基本上意味着在最好的播种情况下你将无法获得超过30个线程,因为任何其他线程只会重复其他一些线程的结果。乘数2来自这样一个事实,即每个点使用序列中的两个元素,因此如果将序列移动一个元素,则可以获得不同的点集。
最终解决方案是完全放弃你的PRNG并坚持Mersenne twister MT19937之类的东西,其周期长度为2 19937 - 1,并且是一种非常强大的播种算法。如果您在问题中未能使用其他PRNG,请至少修改LCG的常量以匹配iters
中使用的常量:
rand()
请注意int rng_int(void) {
static int state = 1;
// & 0x7fffffff is equivalent to modulo with RNG_MOD = 2^31
return (state = (state * 1103515245 + 12345) & 0x7fffffff);
}
不是一个好的PRNG - 它仍然很糟糕。它比你的代码中使用的好一点。
答案 1 :(得分:0)
尝试以下代码。它为每个线程创建一个私有状态。我使用at rand_r
函数Why does calculation with OpenMP take 100x more time than with a single thread?
编辑:我使用Hristo的一些建议更新了我的代码。我使用了threadprivate(第一次)。我还使用了更好的rand函数,可以更好地估计pi,但它还不够好。
一个奇怪的事情是我必须在threadprivate之后定义函数rng_int
否则我得到一个错误“错误:'状态'在第一次使用后声明'threadprivate'”。我应该问一个关于这个的问题。
//gcc -O3 -Wall -pedantic -fopenmp main.c
#include <omp.h>
#include <stdio.h>
#define RNG_MOD 0x80000000
int state;
int rng_int(void);
double rng_doub(double range);
int main() {
int i, numIn, n;
double x, y, pi;
n = 1<<30;
numIn = 0;
#pragma omp threadprivate(state)
#pragma omp parallel private(x, y) reduction(+:numIn)
{
state = 25234 + 17 * omp_get_thread_num();
#pragma omp for
for (i = 0; i <= n; i++) {
x = (double)rng_doub(1.0);
y = (double)rng_doub(1.0);
if (x*x + y*y <= 1) numIn++;
}
}
pi = 4.*numIn / n;
printf("asdf pi %f\n", pi);
return 0;
}
int rng_int(void) {
// & 0x7fffffff is equivalent to modulo with RNG_MOD = 2^31
return (state = (state * 1103515245 + 12345) & 0x7fffffff);
}
double rng_doub(double range) {
return ((double)rng_int()) / (((double)RNG_MOD)/range);
}
查看结果(并编辑并运行代码)