c - 为什么 OpenMP 无法对这些数字求和?

标签 c openmp

考虑以下最小的 C 代码示例。当使用 export OMP_NUM_THREADS=4 && gcc -fopenmp minimal2.c && ./a.out(OS X 10.11 上的自制 GCC 5.2.0)编译和执行时,这通常会产生正确的行为,即 7具有相同编号的行。但有时,会发生这种情况:

[ ] bsum=1.893293142303100e+03
[1] asum=1.893293142303100e+03
[2] asum=1.893293142303100e+03
[0] asum=1.893293142303100e+03
[3] asum=3.786586284606200e+03
[ ] bsum=1.893293142303100e+03
[ ] asum=3.786586284606200e+03
equal: 0

它看起来像一个竞争条件,但我的代码对我来说似乎没问题。我做错了什么?

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

#ifdef _OPENMP
#include <omp.h>
#define ID omp_get_thread_num()
#else
#define ID 0
#endif
#define N 1400

double a[N];

double verify() {
    int i;
    double bsum = 0.0;
    for (i = 0; i < N; i++) {
        bsum += a[i] * a[i];
    }
    fprintf(stderr, "[ ] bsum=%.15e\n", bsum);
    return bsum;
}

int main(int argc, char *argv[]) {
    int i;
    double asum = 0.0, bsum;
    srand((unsigned int)time(NULL));
    //srand(1445167001); // fails on my machine
    for (i = 0; i < N; i++) {
        a[i] = 2 * (double)rand()/(double)RAND_MAX;
    }
    bsum = verify();
    #pragma omp parallel shared(asum)
    {
        #pragma omp for reduction(+: asum)
        for (i = 0; i < N; i++) {
            asum += a[i] * a[i];
        }
        fprintf(stderr, "[%d] asum=%.15e\n", ID, asum);
    }
    bsum = verify();
    fprintf(stderr, "[ ] asum=%.15e\n", asum);
    return 0;
}

编辑: Gilles 提醒我,从第 15 个有效数字开始的错误是正常的,因为我高估了 double 。我也无法在 Debian 机器上用正确数字的 2 倍重现错误行为,因此这可能与自制 gcc 或 Mac 相关。

我遇到了类似的问题 here , 但两者似乎没有关系(至少在我看来),所以我将其作为一个单独的问题开始。

最佳答案

我强烈怀疑这是因为 floating-point addition is not associative .因此,OpenMP 以不同的顺序对乘法求和,产生略有不同的结果。

OpenMP 4.0 spec, section 1.3 Execution Model说:

For example, a serial addition reduction may have a different pattern of addition associations than a parallel reduction. These different associations may change the results of floating-point addition.

参见 OpenMP parallel for reduction delivers wrong results以获得建议的解决方案。

关于c - 为什么 OpenMP 无法对这些数字求和?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33190809/

相关文章:

c - EVP_DecryptFinal 缺少 8 个字节

c - 每个链接列表都有较小的数组,这在 C 中有意义吗?

c - C语言中如何从char中获取int

c++ - 在嵌套循环中使用 OpenMP reduction 子句

java - Eclipse "Show Proposal"组合键不适用于 Java 或 C

c - 为什么我不能将 scanf 语句直接分配给 C 中的变量?

c - 如何安全地将 for 循环与内部的 memcpy 并行

fortran - 为什么 OpenMP 原子性和关键性不能给出正确的结果?

multithreading - 使用线程时的内存管理

concurrency - 使用 OpenMP 的数组中的最大值