OpenMP for循环不能给出正确的结果

时间:2011-12-09 07:44:09

标签: c multithreading gcc for-loop openmp

抱歉,我刚刚开始学习OpenMP,所以我有点困惑。 我正在分析我的分子动力学模拟,在一部分代码中我试图找到水分子(或离子)和蛋白质之间的最近距离。这是非常耗时的部分,因为我有大约500000个原子和大约25000个帧。单CPU需要1周(对于一组计算而言不仅是距离)。

我通过OpenMP将这部分代码更改为并行,但它确实很快但有一点bug;与单CPU代码相比,90%的结果(距离)是正确的,10%是错误的。 这是我的代码中计算最近距离的部分:

    ...
    for (i=0; i< number of frames(25000) )
    ...
    // XP,YP,ZP protein coordinates; malloc allocation in the code
    // XI,YI,ZI Sodium molecule coordinates; malloc allocation
    // LX,LY,LZ the dimension of simulation box, malloc allocation
    // dimI defined as a temporary closest distance, filled with very large constant,
    // malloc allocation
    // NSOD number of Sodium molecules
    // rhos keeping the closest distance for each Sodium for each frame.
    …
    ...int l=0,kk=0;
#pragma omp parallel for shared(XI,YI,ZI,XP,YP,ZP,LX,LY,LZ,qq,dimI,distI,rhos,xmin,ymin,zmin,i) private(kk,l)
      for (l=0; l < NSOD; l++){
        // this part relocates every thing inside a box with dimension LX*LY*LZ. xmin, ymin and zmin are the boundaries of the box.
        if (XI[l]!=0.0 || YI[l]!=0.0 || ZI[l]!=0.0){
          if (XI[l] < xmin) XI[l] += ceil((xmin - XI[l])/LX[i-1]) * LX[i-1];
          if (XI[l] > xmax) XI[l] -= ceil((XI[l] - xmax)/LX[i-1]) * LX[i-1];
          if (YI[l] < ymin) YI[l] += ceil((ymin - YI[l])/LY[i-1]) * LY[i-1];
          if (YI[l] > ymax) YI[l] -= ceil((YI[l] - ymax)/LY[i-1]) * LY[i-1];
          if (ZI[l] < zmin) ZI[l] += ceil((zmin - ZI[l])/LZ[i-1]) * LZ[i-1];
          if (ZI[l] > zmax) ZI[l] -= ceil((ZI[l] - zmax)/LZ[i-1]) * LZ[i-1];
        }
        for (kk=0; kk<NP; kk++){

         if ( ( XP[kk]!=0. || YP[kk]!=0. || ZP[kk]!=0. )  ){
           distI[l] = sqrt((XI[l]-XP[kk])*(XI[l]-XP[kk]) + (YI[l]-YP[kk])*(YI[l]-YP[kk]) + (ZI[l]-ZP[kk])*(ZI[l]-ZP[kk]) );
           if (distI[l] < dimI[l] ) {
              dimI[l] = distI[l];
           }
         }
        }
        distI[l] = dimI[l];
        rhos[qq][l] = dimI[l];

}     #pragma omp barrier         ...

请您告诉我并行化后我的代码有什么问题?为什么只有在某些情况下它会给出错误答案而不是所有情况?我非常感谢您的意见和建议。我在linux上使用gcc。 非常感谢,

干杯, Arash的

3 个答案:

答案 0 :(得分:2)

处理浮点数时,

可能不是一个好主意
if (XI[l]!=0.0 || YI[l]!=0.0 || ZI[l]!=0.0){

相反,你应该与epsilon(一些非常小的数字)进行比较

if (fabs(XI[l]) > epsilon || ...
否则,这可能会导致问题。

答案 1 :(得分:1)

除了安德斯的回答。

除了使用实数进行数学计算之外,由于舍入误差,浮点运算不是关联的。 OpenMp改变了循环评估的顺序,因此结果通常会略有不同。您必须进行灵敏度分析,以确定您对结果的期望精度,并确定您使用(或不使用)OpenMP计算的内容是否在可容忍的范围内。

Numerics是一门艺术。

答案 2 :(得分:0)

我的问题已经偶然解决了,虽然我讨厌这种盲目解决问题;因此我找不到任何解释。 正如我所提到的,我应该找到蛋白质 - 水,蛋白质 - 钠和蛋白质 - 氯化物之间的最近距离。为了完成这项工作,我需要一个临时浮点变量来比较新距离与前一个距离,如果它更小,则替换最小距离。我认为,因为我将在不同的线程上运行它,所以在我的代码中为这个比较定义一维浮点数组更安全。我定义了一维浮点数组dimI[l],并在初始化期间用一个非常大的浮点数填充它。

我在我的代码中尝试了openmp命令中的共享和私有变量的组合,最后我用普通的float替换了这个1D数组,并将其定义为私有变量;令人惊讶的是它解决了这个问题,现在它完美无缺。

为什么1D数组不能作为临时变量?似乎定义一维阵列更安全;无论线程数如何,如下kk=0..NSOD是私有的,而所有其他都是共享变量

  Thread 0            T1                  T2                      
  l=0 ..10         l=11..20            l=21..30               
kk1=0..NSOD       kk2=0..NSOD         kk3=0..NSOD  
XI[l],XP[kk1]    XI[l] ,XP[kk2]      XI[l],XP[kk3]
  distI[l]          distI[l]            distI[l]
  dimI[l]           dimI[l]             dimI[l]

上述方法有什么问题?

干杯, Arash的