考虑以下最小的 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/