OpenMP 和 #pragma omp atomic

9

我在使用OpenMP时遇到了问题。MSVS编译器提示我"pragma omp atomic has improper form",但我不知道原因。代码如下(使用积分方法计算π值):

#include <stdio.h>
#include <time.h>
#include <omp.h>

long long num_steps = 1000000000;
double step;

int main(int argc, char* argv[])
{
    clock_t start, stop;
    double x, pi, sum=0.0;
    int i;
    step = 1./(double)num_steps;
    start = clock();

    #pragma omp parallel for
    for (i=0; i<num_steps; i++)
    { 
        x = (i + .5)*step;
        #pragma omp atomic //this part contains error
        sum = sum + 4.0/(1.+ x*x);  
    }

    pi = sum*step;
    stop = clock();

    // some printf to show results
return 0;
}
4个回答

14

根据当前OpenMP标准,您的程序是完全符合语法要求的(例如,使用GCC 4.7.1进行编译不需要修改)。但是,x 应该被声明为 private (这不是一个语法错误,而是一个语义错误)。不幸的是,微软Visual C++ 实现了一个非常旧的OpenMP规范(2002年3月的2.0版本),仅允许以下语句在 atomic 结构中使用:

x binop= expr
x++
++x
x--
--x

后续版本包括 x = x binop expr,但MSVC甚至在VS2012中也只停留在OpenMP 2.0版本。作为对比,当前的OpenMP版本是3.1,并且我们预计在接下来的几个月内会推出4.0版本。

在OpenMP 2.0中,您的语句应该如下所示:

#pragma omp atomic
sum += 4.0/(1.+ x*x);

但正如已经注意到的那样,更好(并且通常更快)的方法是使用约简:

#pragma omp parallel for private(x) reduction(+:sum)
for (i=0; i<num_steps; i++)
{ 
    x = (i + .5)*step;
    sum = sum + 4.0/(1.+ x*x);  
}

(您还可以编写sum += 4.0 /(1. + x * x); )


3

尝试将 sum = sum + 4.0/( 1. + x*x ) 改为 sum += 4.0/(1.+ x*x),但我担心这也不起作用。您可以尝试像这样分开工作:

x = (i + .5)*step;
double xx = 4.0/(1.+ x*x);
#pragma omp atomic //this part contains error
sum += xx;

这应该可以工作,但我不确定它是否符合你的需求。


我不得不更改结构。'#pragma omp reduction(+:sum)' 是正确的。 - krzakov
我建议使用原子操作的解决方案。这是官方参考网站上的解释方式。如果问题得到解决,那就太好了。 - NiVeR
@krzakov,#pragma omp reduction(+:sum) 是不正确的。编译器很可能会忽略它,并且您会遇到一个由数值不精确性隐藏的危险数据竞争。 reduction是一个子句,它必须应用于另一个OpenMP指令 - 在您的情况下是parallel for指令。 - Hristo Iliev

1

替换:

#pragma omp atomic

使用#pragma omp reduction(+:sum)#pragma omp critical

但是我认为#pragma omp reduction会更好,因为你可以将sum+=Var;添加到里面。

像这样做:

x = (i + .5)*step;
double z = 4.0/(1.+ x*x);
#pragma omp reduction(+:sum)
sum += z;

这很有帮助。比我之前想象的更高级。我应该阅读一份规范。 - krzakov
2
“reduction”是一个数据共享子句,适用于parallel指令(或组合parallel for),而不是单独的指令。 - Hristo Iliev

0

你可能需要重新了解一下 #pragma,而不是真正解决你的问题。

#pragma 是一组非标准编译器特定、大多数时候是平台/系统特定的指令集 - 这意味着在具有相同操作系统或不同设置的计算机上,行为可能会有所不同。

因此,任何与 pragma 相关的问题只能通过查看您选择的平台的编译器的官方文档来解决,这里提供了两个链接。

对于标准的 C/C++,不存在 #pragma

是的,我正在使用支持OpenMP的MSVS。顺便说一下,我正在使用标准的MSVS编译器。 - krzakov
3
幸运的是,OpenMP编译指示在一系列标准文档中定义得非常清晰,任何符合要求的C/C++编译器都应该完全遵循相同的OpenMP指示语法。 - Hristo Iliev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接