OpenMP减法归约操作符“-”

3
int main()
{
    int a=0;
    omp_set_num_threads(2);
    #pragma omp parallel reduction(+ : a)
    {
        a = omp_get_thread_num()+1;
    }
    std::cout << "Output:" << a;
    return 1;
}

我使用 OpenMP reduction 得到错误的输出... 对于以下代码,reduction(+) 给出 threadnum() 的和作为输出结果,但是当我使用 reduction (-) 时,它给出相同的输出结果... 我得到的输出结果为 "+" 和 "-" 都是 3。

我只能观察到nCount被初始化为0,每个线程递增1,所以最终所有的值相加得到4。但仍然不明白为什么减法不起作用。 - noorul
是的,但在这种情况下,对于特定线程的nCOunt,+=1和=1都会产生相同的输出。最后,因为我们已经提到了reduction(0),所以所有内容都会被添加。我的理解正确吗? - noorul
2个回答

8

OpenMP 函数中的减少操作如下:每个线程都有其本地副本的减少变量,例如 +=-= 等操作会影响本地副本。然后在最后执行以下减少操作:

a = init_value op a_0 op a_1 op a_2 op ... op a_(N-1)

其中N是线程数,init_value是约简操作的初始值。加法和减法约简操作的初始值都为0(零)。然而,减法有一个陷阱,它在OpenMP标准中作为对最终值形成方式(§2.9.3.6 reduction子句)的注释进行了解释:

(减法约简的部分结果被加在一起形成最终值。)

这意味着使用reduction(-:a)仍然会将所有私有值相加,即加法和减法约简操作是等效的。这是正确的实现方式,因为假设-约简只与var -= exprvar = var - expr表达式配对。因此,您的程序不符合标准,并且由于没有深入研究OpenMP的复杂细节,您得到了应有的结果。

带有reduction(-:a)的正确示例为:

#pragma omp parallel reduction(-:a)
{
    a -= omp_get_thread_num() + 1;
}

谢谢你的好回答。你提到它在2.9.3.6条款中有解释,你能否发布链接... - noorul
从这里选择您的特定版本:http://openmp.org/wp/openmp-specifications/。§2.9.3.6是OpenMP 3.1标准中的特定章节号。在旧版本中,它是不同的-只需查找内容中的“reduction clause”。 - Hristo Iliev
这是一个很好的答案,所以我感到有点不好意思挑剔,但你在最后说“执行以下约简操作”。据我所知,在for循环中没有屏障(nowait)进行约简,因此约简并非全部在最后完成。但也许我错了。当然,这对于这个特定的问题没有任何影响。 - user2088790
缩减操作在并行区域的末尾执行。在您的情况下,这与循环的结束重合,因为使用了组合的“parallel for”结构。使用不同部分结果的顺序可能取决于不同线程到达区域末尾的顺序。 - Hristo Iliev

0

使用减号进行缩减总是很棘手的,因为减法运算符不满足交换律...最好的方法是将所有数值相加,然后对结果取反;这样代码读者就可以清晰地理解代码,而不必去查看标准!


没错,我正在探索OpenMP的工作原理,结果引发了这些疑问。 - noorul

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