我遇到了简单的代码问题。我正在尝试将OpenMP与GFortran一起使用。 x
下面的代码结果应该与没有!$OMP
语句的AND相同,因为并行代码和串行代码应该输出相同的结果。
program test
implicit none
!INCLUDE 'omp_lib.h'
integer i,j
Real(8) :: x,t1,t2
x=0.0d0
!$OMP PARALLEL DO PRIVATE(i,j) shared(X)
Do i=1,3
Write(*,*) I
!pause
Do j=1,10000000
!$OMP ATOMIC
X=X+2.d0*Cos(i*j*1.0d0)
end do
end do
!$OMP END PARALLEL Do
write(*,*) x
end program test
但奇怪的是我得到x
的以下结果:
并行:-3.17822355415XXXXX
序列号:-3.1782235541569084
其中XXXXX
是一些随机数字。每次运行序列代码时,都会得到相同的结果(-3.1782235541569084)。我该如何解决?这个问题是由于某些OpenMP工作精度选项造成的吗?
答案 0 :(得分:4)
浮点运算不是严格的关联。在f-p算术中,a+(b+c)==(a+b)+c
和a*(b*c)==(a*b)*c
都不是真的,因为它们都是实数算术。这是众所周知的,并在SO的其他问题的答案和网上其他有名的地方广泛解释。我在此不再详述这一点。
在编写程序时,计算X
的最终值的操作顺序是不确定的,即它可能(并且可能确实)因执行而异。 atomic
指令一次只允许一个线程更新X
,但它不会对到达指令的线程施加任何排序约束。
鉴于程序中计算的性质,我认为你在串行和并行执行之间看到的差异可能完全由这种非确定性来解释。
在考虑“修复”之前,首先要确定这是一个问题。是什么让你认为序列号的答案是一个真正的答案?如果你要向后运行循环(仍然是连续的)并得到一个不同的答案(很可能),你正在寻找哪个答案?在很多科学计算中,可能是OpenMP的核心领域,可用的数据和使用的数值方法根本不支持超出少数重要数字的程序结果准确性的断言。
如果你仍然认为这是一个需要修复的问题,最简单的方法就是简单地取出OpenMP指令。
答案 1 :(得分:1)
要添加高性能标记所说的,另一个差异来源是编译器可能已发出x87 FPU指令来进行数学运算。 x87使用80位内部精度,优化后的串行代码只会在实际将最终值写入X
的内存位置之前使用寄存器算法。在并行的情况下,由于X
是共享变量,因此在每次迭代时都会更新内存位置。这意味着80位x87 FPU寄存器被刷新到64位存储器位置然后读回,因此每次迭代都会丢失一些精度位,这就会增加观察到的差异。
如果现代64位CPU与发出SIMD指令的编译器一起使用,则不会出现此效果,例如: SSE2 +或AVX。这些仅使用64位内部精度然后仅使用寄存器寻址不会产生比在每次迭代中刷新和重新加载内存值更好的精度。在这种情况下,差异来自非关联性,如高性能标记所解释。
这些影响非常令人期待并且通常会被考虑在内。它们经过深入研究和理解,如果你的CFD算法在并行运行时发生故障,那么算法在数值上非常不稳定,即使在串行情况下,我也不会信任它给出的结果。
顺便说一下,实现循环的更好方法是使用简化:
!$OMP PARALLEL DO PRIVATE(j) REDUCTION(+:X)
Do i=1,3
Write(*,*) I
!pause
Do j=1,10000000
X=X+2.d0*Cos(i*j*1.0d0)
end do
end do
这将允许编译器为每个线程生成寄存器优化代码,然后精度损失仅在线程求和其局部部分值的最后才会发生,以便获得{{1}的最终值}。
答案 2 :(得分:-2)
我使用了与您一起订购的条款和此工作。带有此条款的工作就是以串行方式运行代码。