在C代码中使用OpenMP后输出错误

时间:2018-06-27 20:47:45

标签: c openmp

考虑此程序:

#include <stdio.h>
#ifdef _OPENMP
#include <omp.h>
#endif

int main(int argc, char *argv[]) {
    int i, ng, igx1 = 6, dgx = 1, igx2 = 599;
    int *ix_recv;

    ng = (igx2 - igx1) / dgx + 1;
    ix_recv = (int *) malloc(ng); 
    memset(ix_recv, 0, ng * sizeof(int));

    ix_recv[0] = igx1 - 1;

#ifdef _OPENMP
    #pragma omp parallel for default(none) private(i) shared(ix_recv,dgx,ng)
#endif        
    for (i = 0; i < ng - 1; i++) { 
        ix_recv[i + 1] = ix_recv[i] + dgx;
    }
}

在不使用OpenMP的情况下,ix_recv的最终值为5,6,7,8,...,597,598,这正是我所期望的。但是,使用OpenMP时,结果将完全错误。

出什么问题了?

1 个答案:

答案 0 :(得分:1)

在循环中,每次迭代都依赖于前一个迭代:

for (i = 0; i < ng - 1; i++) { 
    ix_recv[i + 1] = ix_recv[i] + dgx;
}

请注意分配给ix_recv[i + 1]的值如何取决于ix_recv[i]的值。仅当从期望值中读取期望值时,才将期望值分配给前者,而期望值在串行执行中发生在前一次迭代中。而且该迭代取决于之前的迭代,依此类推,除了您已正确安排i == 0迭代依赖于循环范围之外的先前初始化之外。

现在,假设您将循环迭代分解为多个块,您可以将这些块分配给并行运行的各个线程。从i == 0开始只有一个线程可以获取块。其他线程的第一块从其他地方开始,并且如果它们确实是并发执行的,则这些其他线程以ix_recv的前一个成员中的错误值开始。那么就是垃圾进/垃圾出的情况了。

这种特殊的依赖关系很容易发现,但是一般的依赖关系分析是一个难题,OpenMP将其交给程序员处理。您可能会发现关于这个主题的讨论具有启发性:http://pages.tacc.utexas.edu/~eijkhout/pcse/html/openmp.html#Dependencyanalysis。如果您不删除或满足所有依赖性,那么您的并行程序很可能会产生错误的结果。

在您的特定情况下,有多种方法可以解决依赖性问题。这是一个:

#ifdef _OPENMP
    #pragma omp parallel for
#endif        
    for (int i = 0; i < ng; i++) { 
        ix_recv[i] = igx1 - 1 + dgx * i;
    }

请注意,ix_recv的每个元素的值仅取决于i和循环不变式。