无法弄清楚在C中的OPENMP程序中发生竞争情况的位置

时间:2019-03-03 20:55:08

标签: c openmp

我试图将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 );

   }

我尝试添加原子编译法,但这也无济于事。

任何帮助将不胜感激:)。

1 个答案:

答案 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]。