OpenMP - 关键部分+缩减

时间:2016-02-03 11:35:40

标签: c multithreading openmp critical-section reduction

我目前正在使用C和OpenMP学习并行编程。 我想编写简单的代码,其中两个共享值由多个线程递增。 首先,我使用了 reduction 指令,它的工作原理就是这样。然后我转而使用批判性指令来启动关键部分 - 它也有效。 出于好奇,我试图合并这两个解决方案并检查行为。我期待两个有效的,相等的值。

代码:

#include <stdio.h>
#include <stdlib.h>
#include "omp.h"

#define ITER 50000

int main( void )
{
    int x, y;
    #pragma omp parallel reduction(+:x,y)
    {
       #pragma omp for
       for (int i = 0; i < ITER; i++ )  
       {
            x++;
            #pragma omp critical
            y++;
       }
    }

    printf("non critical = %d\ncritical = %d\n", x, y);
    return 0;
}

输出:

  

非关键= 50000
  critical = 4246432

当然,当涉及到关键的&#39;时,输出是随机的。 (变量y),另一个表现符合预期,总是50000.

x 的行为是可以理解的 - 缩减会使其在单线程范围内变为私有。在将线程的增量值相加并传递给非局部x之后。

我不明白的是 y 的行为。它是私有的,就像 x 一样,但它也在批评部分内,所以它有多个原因&#39;从其他线程无法访问。然而,我认为,恰好是竞争条件。 批判性是否以某种方式使 y 公开(共享)?

我知道这段代码没有任何意义,因为它只能使用减少 / 关键中的一个。我只想知道这种行为背后的原因。

2 个答案:

答案 0 :(得分:6)

您的代码只显示未定义的行为,critical的存在与您获得错误结果无关。

  

严重是否以某种方式使 y 公开(共享)?

不,它没有。它只会通过阻止并发执行线程来减慢循环。

您缺少的是缩减操作的结果与缩小变量的初始值相结合,即与变量在并行区域之前具有的值相结合。在您的情况下,xy都有随机初始值,因此您会得到随机结果。在您的情况下,初始值x恰好为0,这就是为什么您获得正确结果的原因就是UB。初始化xy会使您的代码按预期运行。

OpenMP规范声明:

  

reduction子句指定 reduction-identifier 和一个或多个列表项。对于每个列表项,在每个隐式任务或SIMD通道中创建一个私有副本,并使用 reduction-identifier 的初始化值初始化。在区域结束后,原始列表项 已更新,并使用与 reduction-identifier 关联的组合器使用私有副本的值

以下是使用4个线程执行原始代码:

$ icc -O3 -openmp -std=c99 -o cnc cnc.c
$ OMP_NUM_THREADS=1 ./cnc
non critical = 82765
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 82765
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 82765
critical = 50194
$ OMP_NUM_THREADS=4 ./cnc
non critical = 82767
critical = 2112072800

第一次使用一个线程运行表明它不是由于数据竞争造成的。

使用int x=0, y=0;

$ icc -O3 -openmp -std=c99 -o cnc cnc.c
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000
$ OMP_NUM_THREADS=4 ./cnc
non critical = 50000
critical = 50000

答案 1 :(得分:5)

您的代码的主要问题是xy未初始化。第二个问题是关键部分中使用的变量应该是shared而不是减少变量,尽管这只会影响性能,而不是正确性。

我已更正您的代码并对其进行修改,以演示reducecriticalatomic如何产生相同的结果。

来源

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

int main(int argc, char* argv[])
{
    int iter = (argc>1) ? atoi(argv[1]) : 50000;
    int r=0, c=0, a=0;

    printf("OpenMP threads = %d\n", omp_get_max_threads() );

    #pragma omp parallel reduction(+:r) shared(c,a)
    {
        #pragma omp for
        for (int i = 0; i < iter; i++ ) {
            r++;
            #pragma omp critical
            c++;
            #pragma omp atomic
            a++;
        }
    }
    printf("reduce   = %d\n"
           "critical = %d\n"
           "atomic   = %d\n", r, c, a);
    return 0;
}

编译

icc -O3 -Wall -qopenmp -std=c99 redcrit.c

输出

OpenMP threads = 4
reduce   = 50000
critical = 50000
atomic   = 50000