我正在尝试编写一个C(gcc)函数,该函数将在跨多个线程运行时计算双精度数组的最大值。我创建了一个大小为omp_get_num_threads
的数组,其中我存储了每个线程的局部最大值,然后最终最大化了这个小数组。代码是(或多或少)以下内容:
int i;
double *local_max;
double A[1e10]; //made up size
#pragma omp parallel
{
#pragma omp master
{
local_max=(double *)calloc(omp_get_num_threads(),sizeof(double));
}
#pragma omp flush //so that all threads point
//to the correct location of local_max
#pragma omp for
for(i=0;i<1e10;i++){
if(A[i]>local_max[omp_get_thread_num()])
local_max[omp_get_thread_num()]=A[i];
}
}
free(local_max);
然而,这会导致段错误,而valgrind会抱怨使用未初始化的变量。事实证明,local_max在进入for
构造之前并未在所有线程中实际更新。我以为#pragma omp flush
应该这样做?如果我用#pragma omp barrier
替换它,一切正常。
有人可以向我解释发生了什么事吗?
答案 0 :(得分:3)
解决您问题的最简单方法是简单地用master
构造替换single
构造,因为哪个线程进行分配并不重要(除非您在NUMA机器上运行) ,但是你还有许多其他事情要担心):
#pragma omp single
{
local_max=(double *)calloc(omp_get_num_threads(),sizeof(double));
}
master
和single
之间的细微差别在于single
末尾存在隐含障碍,而master
末尾没有此类障碍。此隐式屏障使所有其他线程等待,直到执行single
块的线程使其到达块的末尾(除非指定了nowait
子句,这将删除隐式屏障)。使用master
时,必须明确添加障碍。我无法理解为什么OpenMP设计师决定master
不会像single
那样有隐含的障碍。
答案 1 :(得分:1)
您需要设置障碍以确保内存分配已完成。内存分配是一项耗时的操作,当最终的for循环开始运行时,local_max并未指向正确分配的空间。我修改了下面的代码来演示行为。
int i;
double *local_max;
omp_set_num_threads(8);
#pragma omp parallel
{
#pragma omp master
{
for(int k = 0; k < 999999; k++) {} // Lazy man's sleep function
cout << "Master start allocating" << endl;
local_max=(double *)calloc(omp_get_num_threads(),sizeof(double));
cout << "Master finish allocating" << endl;
}
#pragma omp flush
#pragma omp for
for(i=0;i<10;i++){
cout << "for : " << omp_get_thread_num() << " i: " << i << endl;
}
}
free(local_max);
getchar();
return 0;
答案 2 :(得分:0)
更好的是,只需在#pragma omp并行操作之前移动内存分配。无需冲洗,也无需单冲洗,也无需冲洗。