OpenMP循环为同一个串行循环提供不同的结果

myl*_*les 3 c parallel-processing multicore openmp reduction

我有一些串行代码:

double* a = malloc((1000000) * sizeof(double));
double* b = malloc((1000000) * sizeof(double));
double totalA = 0;

for (int i = 0; i < 1000000; i++) {

    if (i == 0) {
        a[i] = sin(i);
    }

    b[i] = sin(i+1);

    if (i < 1000000-1) {
        a[i+1] = b[i];
    }

    totalA += a[i];
}
Run Code Online (Sandbox Code Playgroud)

totalA此串行循环后的输出是0.232883978073.

然后我有一个OpenMP版本(注意:所有变量都重新初始化):

double* a = malloc((1000000) * sizeof(double));
double* b = malloc((1000000) * sizeof(double));
double totalA = 0;

#pragma omp parallel for reduction(+:totalA)
for (int i = 0; i < 1000000; i++) {

    if (i == 0) {
        a[i] = sin(i);
    }

    b[i] = sin(i+1);

    if (i < 1000000-1) {
        a[i+1] = b[i];
    }

    totalA += a[i];
}
Run Code Online (Sandbox Code Playgroud)

但是,totalA此代码的输出是-0.733714826779.

我无法弄清楚为什么它与众不同.

谢谢.


UPDATE

经过一些游戏后,似乎if奇怪地忽略了循环中的语句.if块中的实际语句在循环的所有迭代上执行(就好像该if子句不存在).

例如,将if块更改为:

if (i < 555555) {
    a[i+1] = b[i];
}
Run Code Online (Sandbox Code Playgroud)

似乎完全没有区别.

我仍然不知道这里发生了什么.

Ste*_*ans 5

您的代码包含竞争条件.冲突的语句是a[i+1] = b[i];写入数组的赋值atotalA += a[i];从中读取的语句a.

在您的代码中,无法保证在从该位置读取的迭代之前执行负责写入数组中特定位置的迭代.

为了进一步演示此问题,对包含冲突语句的循环段进行排序可以解决问题(但最有可能破坏性能):

#pragma omp parallel for ordered reduction(+:totalA)    
for (int i = 0; i < 1000000; i++) {

   if (i == 0) {
     a[i] = sin(i);
   }

  b[i] = sin(i+1);

  #pragma omp ordered
  {
    if (i < 1000000-1) {
      a[i+1] = b[i];
    }

    totalA += a[i];
  }
}
Run Code Online (Sandbox Code Playgroud)

最好完全避免这个问题并重写你的程序以摆脱循环携带的依赖:

#define N 1000000

double* a = malloc(N * sizeof(double));
double* b = malloc(N * sizeof(double));
double totalA = 0;

a[0] = sin(0);
totalA += a[0];

#pragma omp parallel for reduction(+:totalA)
for (int i = 0; i < N - 1; i++) {
  b[i] = sin(i + 1);
  a[i + 1] = b[i];
  totalA += a[i + 1];
}

b[N - 1] = sin(N);
Run Code Online (Sandbox Code Playgroud)

最后,请注意,作为sin(0) ? 0.0,陈述

a[0] = sin(0);
totalA += a[0];
Run Code Online (Sandbox Code Playgroud)

可以简单地替换为

a[0] = 0.0;
Run Code Online (Sandbox Code Playgroud)

  • @ 2501通过将#pragma omp放在循环上,你断言它可以并行执行.检查该断言的安全性,而不是编译器的安全性是你的*工作.(一般情况下,编译器无法执行此操作,因为在循环中可能会调用单独编译的代码,甚至在编译器运行时也可能没有编写!) (2认同)