Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C OpenMP-临界截面+;减少_C_Multithreading_Openmp_Critical Section_Reduction - Fatal编程技术网

C OpenMP-临界截面+;减少

C OpenMP-临界截面+;减少,c,multithreading,openmp,critical-section,reduction,C,Multithreading,Openmp,Critical Section,Reduction,我目前正在学习使用C和OpenMP进行并行编程。 我想编写两个共享值被多个线程递增的简单代码。 首先,我使用了reduce指令,它按原意工作。然后我切换到使用临界指令来启动临界部分-它也起作用。 出于好奇,我尝试合并这两种解决方案并检查其行为。我希望有两个有效的相等值。代码: #include <stdio.h> #include <stdlib.h> #include "omp.h" #define ITER 50000 int main( void ) {

我目前正在学习使用C和OpenMP进行并行编程。 我想编写两个共享值被多个线程递增的简单代码。 首先,我使用了reduce指令,它按原意工作。然后我切换到使用临界指令来启动临界部分-它也起作用。 出于好奇,我尝试合并这两种解决方案并检查其行为。我希望有两个有效的相等值。

代码:

#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;
}
#包括
#包括
#包括“omp.h”
#定义ITER 50000
内部主(空)
{
int x,y;
#pragma omp并行归约(+:x,y)
{
#pragma omp for
对于(int i=0;i
输出:

非临界=50000
临界值=4246432

当然,当涉及到“临界”(变量y)时,输出是随机的,另一个行为与预期一致,并且始终为50000

x的行为是可以理解的-reduce使其在单线程范围内是私有的。线程的增量值相加后传递给非本地x

我不明白的是y的行为。它是私有的,就像x一样,但它也在临界部分中,因此它“有不止一个原因”无法从其他线程访问。然而,我认为,发生的是比赛条件。批判的是否以某种方式将y公开(共享)


我知道这段代码毫无意义,因为只使用reduce/critical中的一个就足够了。我只想知道这种行为背后的原因

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

我已经更正了您的代码并对其进行了修改,以演示
reduce
critical
atomic
如何产生相同的结果

来源 输出
您的代码只是表现出未定义的行为,而
critical
的存在与您得到错误结果无关

批判的是否以某种方式将y公开(共享)

不,没有。它只能通过阻止线程的并发执行来降低循环的速度

您缺少的是还原操作的结果与还原变量的初始值相结合,即与平行区域之前的变量值相结合。在您的例子中,
x
y
都具有随机初始值,因此您得到的是随机结果。在您的例子中,初始值
x
恰好是0,这就是为什么您得到正确的结果,因为它只是UB。同时初始化
x
y
会使代码按预期运行

OpenMP规范规定:

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

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

$icc-O3-openmp-std=c99-o cnc.c
$OMP_NUM_螺纹=1。/cnc
非临界=82765
临界值=50000
$OMP_NUM_螺纹=4。/cnc
非临界=82765
临界值=50000
$OMP_NUM_螺纹=4。/cnc
非临界=50000
临界值=50000
$OMP_NUM_螺纹=4。/cnc
非临界=82765
临界值=50194
$OMP_NUM_螺纹=4。/cnc
非临界=82767
临界值=2112072800
第一次使用一个线程运行表明这不是由于数据竞争造成的

intx=0,y=0

$icc-O3-openmp-std=c99-o cnc.c
$OMP_NUM_螺纹=4。/cnc
非临界=50000
临界值=50000
$OMP_NUM_螺纹=4。/cnc
非临界=50000
临界值=50000
$OMP_NUM_螺纹=4。/cnc
非临界=50000
临界值=50000
$OMP_NUM_螺纹=4。/cnc
非临界=50000
临界值=50000

谁初始化变量?看起来UB.OpenMP会在reduce指令中自动为私有副本执行此操作(0表示+和-,1表示*和/),但不会为可见变量执行此操作。如果你初始化它会发生什么?我确信减少会隐式地使减少的值私有化。@你在技术上是正确的。但是,当你可以长篇大论并完全说服自己你所写的东西完全按照你的预期工作时,为什么还要依赖OpenMP隐式行为呢?谢谢你的回答!双重计数解释了很多。然而,我并不完全确定你提到的“未定义的行为”。我在大学上过这门课程,我们的导师从未初始化过缩减值-我被告知它们是由OpenMP以0或1启动的,具体取决于缩减的运算符。Tbh我也这么做了,除了上面的情况之外,它一直都是有效的。编辑:尽管如此,我必须说,当我初始化值x,y而不更改任何其他内容时,代码的工作方式与@2501 suggestedYes相同,但我看到OpenMP为简化变量定义了默认的初始值设定项。但是,ISO C没有本地变量()的默认初始值设定项,这意味着如果未启用OpenMP,则程序具有未定义的行为。你不会想要的。而且,
是关键的#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