使用OpenMP critical和ordered

时间:2014-01-08 10:46:04

标签: fortran openmp

我对Fortran和OpenMP都很陌生,但我正试图找到自己的方向。我有一段代码用于计算我试图并行化的变异函数。然而,我似乎正在获得竞争条件,因为一些结果已经消失了千分之一左右。

问题似乎是减少。使用OpenMP减少工作并给出正确的结果,但它们是不可取的,因为减少实际上发生在另一个子例程中(我将相关的行复制到OpenMP循环中进行测试)。因此,我将减少量放在了一个关键部分,但没有成功。有趣的是,问题只发生在实数上,而不是整数。我已经考虑过添加的顺序是否有任何区别,但它们不应该产生这么大的错误。

只是为了检查,我把所有东西放在ORDERED块中并行执行,这当然会给出正确的结果(尽管没有任何加速)。我也尝试将所有内容放在一个CRITICAL部分,但由于某些原因没有给出正确的结果。我的理解是OpenMP将在进入/退出CRITICAL部分时刷新共享变量,因此不应存在任何缓存问题。

所以我的问题是:为什么关键部分在这种情况下不起作用?

我的代码如下。除 np tm hm gam 之外的所有共享变量都是只读的。

编辑:我试图通过用相同范围内的随机整数替换do循环来模拟多线程引起的随机性(即在循环中生成一对i,j;如果它们是“访问过”,则生成新的并且令我惊讶的是结果相符。然而,经过进一步的检查,我发现我忘了给RNG播种,结果是巧合的。多么尴尬!

TL; DR:结果中的差异是由浮点值的排序引起的。使用双精度可以帮助。

!$OMP PARALLEL DEFAULT(none) SHARED(nd, x, y, z, nzlag, nylag, nxlag, &
!$OMP& dzlag, dylag, dxlag, nvarg, ivhead, ivtail, ivtype, vr, tmin, tmax, np, tm, hm, gam) num_threads(512)
!$OMP DO PRIVATE(i,j,zdis,ydis,xdis,izl,iyl,ixl,indx,vrh,vrt,vrhpr,vrtpr,variogram_type) !reduction(+:np, tm, hm, gam)
  DO i=1,nd        
!$OMP CRITICAL (main)
! Second loop over the data:
    DO j=1,nd

! The lag:
      zdis = z(j) - z(i)
      IF(zdis >= 0.0) THEN
        izl =  INT( zdis/dzlag+0.5)
      ELSE
        izl = -INT(-zdis/dzlag+0.5)
      END IF
 ! ---- SNIP ----

! Loop over all variograms for this lag:

      DO cur_variogram=1,nvarg
        variogram_type = ivtype(cur_variogram)

! Get the head and tail values:

        indx = i+(ivhead(cur_variogram)-1)*maxdim
        vrh   = vr(indx)
        indx = j+(ivtail(cur_variogram)-1)*maxdim
        vrt   = vr(indx)
        IF(vrh < tmin.OR.vrh >= tmax.OR. vrt < tmin.OR.vrt >= tmax) CYCLE

        ! ----- PROBLEM AREA -------
        np(ixl,iyl,izl,1)  = np(ixl,iyl,izl,1) + 1.   ! <-- This never fails
        tm(ixl,iyl,izl,1)  = tm(ixl,iyl,izl,1) + vrt  
        hm(ixl,iyl,izl,1)  = hm(ixl,iyl,izl,1) + vrh
        gam(ixl,iyl,izl,1) = gam(ixl,iyl,izl,1) + ((vrh-vrt)*(vrh-vrt))
        ! ----- END OF PROBLEM AREA -----

        !CALL updtvarg(ixl,iyl,izl,cur_variogram,variogram_type,vrt,vrh,vrtpr,vrhpr)
      END DO
    END DO
    !$OMP END CRITICAL (main)
  END DO
!$OMP END DO
!$OMP END PARALLEL

非常感谢!

2 个答案:

答案 0 :(得分:2)

如果您使用32位浮点数并算算84.2653984.26538之间的差异,那么最低有效位数的1差异完全是可以通过并行浮点算法的非确定性来解释。请记住,32位f-p数字只能播放大约7个十进制数字。

普通浮点运算不是严格关联的。对于真实的(在数学上而不是Fortran意义上)数字(a+b)+c==a+(b+c)但是对于浮点数没有这样的规则。 Wikipedia article on floating-point arithmetic中很好地解释了这一点。

出现非确定性是因为,在使用OpenMP时,您将对操作顺序的控制放弃到运行时。线程之间的值的总和(例如+的减少)将全局和表达式包含在运行时中。同一个OpenMP程序的2次执行不会产生相同到最后一位的结果。

我怀疑即使在一个线程上运行OpenMP程序也可能会产生与等效的非OpenMP程序不同的结果。由于OpenMP可执行文件可用线程数的知识可能会延迟到运行时,因此编译器必须创建并行化的可执行文件,无论它是否最终并行运行。

答案 1 :(得分:1)

高性能标记对浮点和关联性提出了一个有趣的观点。这很容易测试(在C中)。

float a = -1.0E8f, b = 1.0E8f, c = 1.23456f;
printf("sum %f\n", a+b+c);   //output 1.234560
printf("sum %f\n", a+(b+c)); //output 0.000000

但我想指出可以保留OpenMP中的顺序。我在这里讨论了C++ OpenMP: Split for loop in even chunks static and join data at the end

修改

实际上,我混淆了交换性和相关性。如果你有一个关联但不具有交际性的运算符,那么就像我在上面的帖子中那样用OpenMP保存订单。然而,IEEE浮点是可交换的但不是相关的,所以唯一要做的就是打破IEEE并让它成为关联的。