OpenMP for 循环导致错误结果
OpenMP for loop cause wrong result
我想通过部分简单代码来比较openmp的循环性能。但是结果是错误的。
我已经使用缩减来避免竞争条件,但从未奏效。
这是我的代码:感谢您的任何建议
void TestMP_1(){
float afValueTmp[MP_TEST_NUM] = { 0 }; // MP_TEST_NUM = 10000
float sum = 0, sumNoMP = 0;
float fDiff = 0;
double eTDiff = 0;
double t0 = 0;
double t1 = 0;
for (int i = 0; i < MP_TEST_NUM; i++)
{
afValueTmp[i] = i;
}
t0 = (double)getTickCount();
for (int i = 0; i < MP_TEST_NUM; i++)
{
for (int k = 0; k < MP_TEST_NUM; k++); // just for delay
sumNoMP += afValueTmp[i]; // equation 4
}
t0 = ((double)getTickCount() - t0) / getTickFrequency();
t1 = (double)getTickCount();
#pragma omp parallel for reduction( +:sum)
for (int i = 0; i < MP_TEST_NUM; i++)
{
for (int k = 0; k < MP_TEST_NUM; k++); // just for delay
sum += afValueTmp[i];
}
t1 = ((double)getTickCount() - t1) / getTickFrequency();
eTDiff = t0 - t1; // time improve
fDiff = sum - sumNoMP; // check result
printf("%.3f\n", eTDiff);
}
根据您所写的内容和预期的四舍五入,您的结果可能是正确的。
您面临浮点精度问题。请允许我详细说明:
#include <stdio.h>
int main(void)
{
float myOrigNumber = 49995000;
float myNumber = myOrigNumber + 1.;
printf ("orig: %f new: %f diff: %f\n",
myOrigNumber, myNumber, myNumber-myOrigNumber);
return 0;
}
结果将是:
orig: 49995000.000000 new: 49995000.000000 diff: 0.000000
那么,+1
去哪儿了?
float
类型只有7到8位有效数字。它们在哪里并不重要,因为浮点数在内部总是在 Scientific notation 中表示为 x.xxE+yy 表示法,其中 x.xx 有 24 位,而 yy 有 8 位。
数字 49995001 大于 2^24 (16,777,216),因此将四舍五入到最接近可以准确表示的数字,显然是 49995000.
这就是为什么将 double
用于 sum
会减轻您的痛苦。不过,这不是真正的解决方案。归约运算要求运算必须可交换。
但是浮点数加法就不一定了:如果你把 100 乘以 49995000 加到 sum
,结果会和你先加 1 和 49995000,然后再 99 乘以 1 的结果不一样:在第二种情况,以后每隔+1会向下舍入,如上所示。
我想通过部分简单代码来比较openmp的循环性能。但是结果是错误的。
我已经使用缩减来避免竞争条件,但从未奏效。
这是我的代码:感谢您的任何建议
void TestMP_1(){
float afValueTmp[MP_TEST_NUM] = { 0 }; // MP_TEST_NUM = 10000
float sum = 0, sumNoMP = 0;
float fDiff = 0;
double eTDiff = 0;
double t0 = 0;
double t1 = 0;
for (int i = 0; i < MP_TEST_NUM; i++)
{
afValueTmp[i] = i;
}
t0 = (double)getTickCount();
for (int i = 0; i < MP_TEST_NUM; i++)
{
for (int k = 0; k < MP_TEST_NUM; k++); // just for delay
sumNoMP += afValueTmp[i]; // equation 4
}
t0 = ((double)getTickCount() - t0) / getTickFrequency();
t1 = (double)getTickCount();
#pragma omp parallel for reduction( +:sum)
for (int i = 0; i < MP_TEST_NUM; i++)
{
for (int k = 0; k < MP_TEST_NUM; k++); // just for delay
sum += afValueTmp[i];
}
t1 = ((double)getTickCount() - t1) / getTickFrequency();
eTDiff = t0 - t1; // time improve
fDiff = sum - sumNoMP; // check result
printf("%.3f\n", eTDiff);
}
根据您所写的内容和预期的四舍五入,您的结果可能是正确的。
您面临浮点精度问题。请允许我详细说明:
#include <stdio.h>
int main(void)
{
float myOrigNumber = 49995000;
float myNumber = myOrigNumber + 1.;
printf ("orig: %f new: %f diff: %f\n",
myOrigNumber, myNumber, myNumber-myOrigNumber);
return 0;
}
结果将是:
orig: 49995000.000000 new: 49995000.000000 diff: 0.000000
那么,+1
去哪儿了?
float
类型只有7到8位有效数字。它们在哪里并不重要,因为浮点数在内部总是在 Scientific notation 中表示为 x.xxE+yy 表示法,其中 x.xx 有 24 位,而 yy 有 8 位。
数字 49995001 大于 2^24 (16,777,216),因此将四舍五入到最接近可以准确表示的数字,显然是 49995000.
这就是为什么将 double
用于 sum
会减轻您的痛苦。不过,这不是真正的解决方案。归约运算要求运算必须可交换。
但是浮点数加法就不一定了:如果你把 100 乘以 49995000 加到 sum
,结果会和你先加 1 和 49995000,然后再 99 乘以 1 的结果不一样:在第二种情况,以后每隔+1会向下舍入,如上所示。