我对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
非常感谢!
答案 0 :(得分:2)
如果您使用32位浮点数并算算84.26539
和84.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并让它成为关联的。