openmp开启后计算结果错误原因
openmp多线程使用方法十分简单,一般对于for循环只需要加一句#pragma omp parallel for就可以了。
新手使用的时候有时候可能会出现开启omp之后,得到的计算结果是错的,而且多次执行结果还不相同。
1. 其中一个可能的原因是你的代码中不同的线程同时使用某一内存的值并且试图改变它,比如累加操作,这种情况下线程x获取的数据可能不是最新的。
示例1:
参考https://www.cnblogs.com/yangyangcv/archive/2012/03/23/2413335.html
#include <iostream>
int main()
{
int sum = 0;
int a[10] = {1,2,3,4,5,6,7,8,9,10};
#pragma omp parallel for
for (int i=0;i<10;i++)
sum = sum + a[i];
std::cout<<"sum: "<<sum<<std::endl;
return 0;
}
如果我们注释掉#pragma omp parallel for,让程序先按照传统串行的方式执行,很明显,sum = 55。但按照并行方式执行后,sum则会变成其他值,比如在某次运行过程中,sum = 49。其原因是,当某线程A执行sum = sum + a[i]的同时,另一线程B正好在更新sum,而此时A还在用旧的sum做累加,于是出现了错误。
解决方案:
#include <iostream>
int main(){
int sum = 0;
int a[10] = {1,2,3,4,5,6,7,8,9,10};
#pragma omp parallel for reduction(+:sum)
for (int i=0;i<10;i++)
sum = sum + a[i];
std::cout<<"sum: "<<sum<<std::endl;
return 0;
}
上面代码里,我们在#pragma omp parallel for 后面加上了 reduction(+:sum),它的意思是告诉编译器:下面的for循环你要分成多个线程跑,但每个线程都要保存变量sum的拷贝,循环结束后,所有线程把自己的sum累加起来作为最后的输出。
reduction虽然很方便,但它只支持一些基本操作,比如+,-,*,&,|,&&,||等。
示例2
int point[8];
float dist[2];
#pragma omp parallel for
for (int i = 0; i < ORG_FEAT_LEN; i++)
{
point[0] = landmarks[2 * (pointIndices1_[i] - 1)];
point[1] = landmarks[2 * pointIndices1_[i] - 1];
point[2] = landmarks[2 * (pointIndices2_[i] - 1)];
point[3] = landmarks[2 * pointIndices2_[i] - 1];
point[4] = landmarks[2 * (pointIndices3_[i] - 1)];
point[5] = landmarks[2 * pointIndices3_[i] - 1];
point[6] = landmarks[2 * (pointIndices4_[i] - 1)];
point[7] = landmarks[2 * pointIndices4_[i] - 1];
dist[0] = sqrt((point[0] - point[2])*(point[0] - point[2]) + (point[1] - point[3])*(point[1] - point[3]));
dist[1] = sqrt((point[4] - point[6])*(point[4] - point[6]) + (point[5] - point[7])*(point[5] - point[7]));
orgFeat[i] = dist[0] / dist[1];
}
由于point声明在for外部,多个线程共同对其操作的时候可能会出现线程0运行到dist计算的时候,point已经被其他线程给修改了,因此会造成dist计算结果不对。dist声明在外部,在计算orgFeat的时候存在同样的问题。因此应该修正为以下:
#pragma omp parallel for
for (int i = 0; i < ORG_FEAT_LEN; i++)
{
int point[8];
float dist[2];
point[0] = landmarks[2 * (pointIndices1_[i] - 1)];
point[1] = landmarks[2 * pointIndices1_[i] - 1];
point[2] = landmarks[2 * (pointIndices2_[i] - 1)];
point[3] = landmarks[2 * pointIndices2_[i] - 1];
point[4] = landmarks[2 * (pointIndices3_[i] - 1)];
point[5] = landmarks[2 * pointIndices3_[i] - 1];
point[6] = landmarks[2 * (pointIndices4_[i] - 1)];
point[7] = landmarks[2 * pointIndices4_[i] - 1];
dist[0] = sqrt((point[0] - point[2])*(point[0] - point[2]) + (point[1] - point[3])*(point[1] - point[3]));
dist[1] = sqrt((point[4] - point[6])*(point[4] - point[6]) + (point[5] - point[7])*(point[5] - point[7]));
orgFeat[i] = dist[0] / dist[1];
}
这样就没有问题了。
2. 还有个可能的问题参考http://blog.csdn.net/qq_34488063/article/details/53177078
你写成这样了
#pragma omp for
for(i = 0, i < n;i++)
这样的写法是错误的,在openmp for语句中一定要声明好这个变量的作用域,上述的写法i是公用变量,也就是说pragma omp for确实是分配给多个线程等量的任务,然而由于每个线程都对i进行i++的这个操作,有些i的变量就被跳过了,
正确的写法应该是:
#pragma omp for
for(int i = 0, i < n;i++)
推荐阅读
-
openmp开启后计算结果错误原因
-
xdebug开启后错误
-
Mac从睡眠状态开启后连不上WiFi是什么原因如何解决
-
QQ空间打开几秒后空白页,出现“iexplore.exe 应用程序错误”的原因和解决方法
-
PHP网站从Apache转移到Nginx后产生404错误的原因和解决办法
-
解决llvm-9.0开启JIT EVENT后的编译错误'undefined reference to llvm::createLowerSwitchPass()'
-
Mac从睡眠状态开启后连不上WiFi是什么原因如何解决
-
开启AHCI模式后电脑突然蓝屏死机的原因分析及解决方法
-
xdebug开启后错误
-
PHP网站从Apache转移到Nginx后产生404错误的原因和解决办法