【发布时间】:2015-10-17 12:05:11
【问题描述】:
考虑以下最小的 C 代码示例。当使用export OMP_NUM_THREADS=4 && gcc -fopenmp minimal.c && ./a.out(Debian 8 上的 GCC 4.9.2)编译和执行时,这会在我的机器上产生五行带有rho=100(有时也是 200 或 400)的行。所有五行打印的预期输出当然是rho=400。
如果我在// MARKER 插入更多代码或在此处设置障碍,则程序更有可能产生正确的结果。但即使有另一个障碍,它有时也会失败,我的程序也是如此。所以问题似乎是a在进入reduction循环时没有正确初始化。
OpenMP 4.0.0 manual 甚至在第 55 页上声明 除非指定 nowait 子句,否则在循环结构的末尾有一个隐式障碍。 所以此时应该设置 a .这里出了什么问题?我错过了什么吗?
#include <stdio.h>
#ifdef _OPENMP
#include <omp.h>
#define ID omp_get_thread_num()
#else
#define ID 0
#endif
double a[100];
int main(int argc, char *argv[]) {
int i;
double rho;
#pragma omp parallel
{
#pragma omp for
for (i = 0; i < 100; i++) {
a[i] = 2;
}
// MARKER
rho = 0.0;
#pragma omp for reduction(+: rho)
for (i = 0; i < 100; i++) {
rho += ((a[i])*(a[i]));
}
fprintf(stderr, "[%d] rho=%f\n", ID, rho);
}
fprintf(stderr, "[%d] rho=%f\n", ID, rho);
return 0;
}
【问题讨论】:
-
修改
rho时需要互斥体吗? -
我不这么认为。该手册的第 167 页说:reduction 子句指定了一个缩减标识符和一个或多个列表项。对于每个列表项,在每个隐式任务或 SIMD 通道中创建一个私有副本,并使用缩减标识符的初始化器值进行初始化。在区域结束后,使用与缩减标识符关联的组合器将原始列表项更新为私有副本的值。
-
代码对我来说看起来不错。但是,以防万一,我会在
parallel指令级别明确声明private(i)。我知道它应该被私有化,但是......无论如何,这对我来说似乎是一个编译器错误,就像你在omp for末尾没有隐含的barrier一样。也许添加一些明确的也可能会有所帮助。 -
现在我很困惑:我在 GCC 4.9.2 上重现了这个问题,但在 Intel 编译器 16.0.0 上也重现了这个问题(诚然少得多)。我仍然相信代码是正确的,但这动摇了我的信念。