我正在尝试将 sin(x) 从 0 积分到 pi。但是每次我跑
我得到不同输出的程序。我知道这是因为出现了竞争条件,但我无法弄清楚问题出在哪里
这是我的代码:
#include<stdio.h>
#include<stdlib.h>
#include<omp.h>
#include<math.h>
#include<time.h>
#define NUM_THREADS 4
static long num_steps= 10000000;
float rand_generator(float a )
{
//srand((unsigned int)time(NULL));
return ((float)rand()/(float)(RAND_MAX)) * a;
}
int main(int argc, char *argv[])
{
// srand((unsigned int)time(NULL));
omp_set_num_threads(NUM_THREADS);
float result;
float sum[NUM_THREADS];
float area=3.14;
int nthreads;
#pragma omp parallel
{
int id,nthrds;
id=omp_get_thread_num();
sum[id]=0.0;
printf("%d\n",id );
nthrds=omp_get_num_threads();
printf("%d\n",nthrds );
//if(id==0)nthreads=nthrds;
for (int i = id; i < num_steps; i=i+nthrds)
{
//float y=rand_generator(1);
//printf("%f\n",y );
float x=rand_generator(3.14);
sum[id]+=sin(x);
}
//printf(" sum is: %lf\n", sum);
//float p=(float)sum/num_steps*area;
}
float p=0.0;
for (int i = 0; i <NUM_THREADS; ++i)
{
p+=(sum[i]/num_steps)*area;
}
printf(" p is: %lf\n",p );
}
我尝试添加 pragma atomic 但它也没有帮助。
任何帮助将不胜感激:)。
最佳答案
问题来自于rand()
的使用。 rand()
不是线程安全的。原因是它对所有调用使用一个公共(public)状态,因此对竞争很敏感。 Using stdlib's rand() from multiple threads
有一个称为rand_r()
的线程安全随机生成器。状态不是将 rand 生成器状态存储在隐藏的全局变量中,而是函数的参数,并且可以在线程本地呈现。
你可以这样使用它
float rand_generator_r(float a,unsigned int *state )
{
//srand((unsigned int)time(NULL));
return ((float)rand_r(state)/(float)(RAND_MAX)) * a;
}
在你的并行 block 中,添加:
unsigned int rand_state=id*time(NULL); // or whatever thread dependent seed
在你的代码调用中
float x=rand_generator(3.14,&rand_state);
它应该可以工作。
顺便说一句,我的印象是有一个false sharing在您的代码中应该会降低性能。
float sum[NUM_THREADS];
它被所有 线程修改并且很可能存储在单个缓存行中。 每个 存储(并且有很多存储)都会在所有其他缓存中创建一个无效,这可能会显着降低您的性能。
您应该确保这些值位于不同的缓存行中:
#define CACHE_LINE_SIZE 64
struct {
float s;
char padding[CACHE_LINE_SIZE - sizeof(float)];
} sum_nofalse_sharing[NUM_THREADS];
并在您的代码中,累积到 sum_nofalse_sharing[id].s
或者,在并行 block 中创建一个本地求和并将其值写入末尾的 sum[id]。
关于c - 无法弄清楚在c中的OPENMP程序中发生竞争条件的位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54973651/