我试图将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 );
}
我尝试添加原子编译法,但这也无济于事。
任何帮助将不胜感激:)。
答案 0 :(得分:3)
问题来自使用rand()
。 rand()
是不是线程安全的。原因是它对所有呼叫都使用公共状态,因此对种族敏感。 Using stdlib's rand() from multiple threads
有一个称为rand_r()
的线程安全随机生成器。代替将rand生成器状态存储在隐藏的全局var中,该状态是函数的参数,可以将其呈现为线程局部。
您可以像这样使用它
float rand_generator_r(float a,unsigned int *state )
{
//srand((unsigned int)time(NULL));
return ((float)rand_r(state)/(float)(RAND_MAX)) * a;
}
在您的并行块中,添加:
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];
它由 all 个线程修改,实际上很可能存储在单个缓存行中。 每个存储区(并且有很多存储区)都会在所有其他缓存中创建一个无效项,这可能会大大降低性能。
您应该使用以下命令确保值位于不同的缓存行中:
#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
或者,在并行块中创建一个本地总和,然后将其值写入末尾的sum [id]。