Openmp没有更新
问题描述:
我试着编写一个C(gcc)函数,它将在多线程中运行时计算双精度数组的最大值。我创建了一个大小为omp_get_num_threads
的数组,其中我存储了每个线程的局部最大值,然后才最终使这个小数组最大化。该代码是(或多或少)以下:Openmp没有更新
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的的抱怨。事实证明,在输入for
构造之前,local_max实际上并未在所有线程中更新。我认为#pragma omp flush
应该这样做?如果我用#pragma omp barrier
代替它,一切正常。
有人可以向我解释发生了什么事吗?
答
您需要设置屏障来确保内存分配已完成。内存分配是一项耗时的操作,当您的最终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;
答
您的问题,最简单的方法是简单地用一个single
一个替代master
结构,因为它并不真正的问题是哪个线程将使分配(除非你是一个NUMA机器上运行,但你也有很多其他的事情担心):
#pragma omp single
{
local_max=(double *)calloc(omp_get_num_threads(),sizeof(double));
}
master
和single
之间的细微差别是,有在single
结束的隐式屏障,而没有这种障碍在master
末存在。这个隐式屏障使所有其他线程等待,直到执行块的线程已经到达块的末尾(除非指定了nowait
子句,这将删除隐式屏障)。使用master
必须明确添加屏障。这超出了我的理解,为什么OpenMP设计人员做出这样的决定:master
不会像single
那样具有隐含的障碍。
非常感谢你,这很有道理。我遇到的一个后续问题是,当线程遇到'flush'构造时,是否所有线程同时进行同步?他们在哪里恢复执行? – Ivan 2013-02-16 00:44:30
'flush'是一个棘手的问题,并且倾向于让我们认为所有的线程都会停止并同步,然后所有的线程都会继续愉快地执行。但这是错误的。当线程遇到'flush'时,它会同步并恢复执行。您可以使用omp_get_wtime来打印执行时间,并在代码中添加一些随机睡眠呼叫,以查看其执行方式。我建议你避免使用'flush',因为它增加了相当高的复杂性。 – ikikenis 2013-02-16 01:06:58
这是否意味着其他线程不会与遇到'flush'的同时同步?他们在访问一个变量的过程中是什么,但在并行构造的不同部分呢? – Ivan 2013-02-16 01:20:30