首先我编译了没有-fopenmp
的代码,然后运行代码,获得了一个基准的串行结果。
其次我考虑使用OpenMP加速我的代码。
有两个奇怪的结果: 1.结果
!$OMP CRITICAL
p1=p1+1
!$OMP END CRITICAL
与序列结果略有差异(1%)。我的代码不包含随机数,所以它一定是错的。
2.如果我将!$OMP CRITICAL
替换为!$OMP ATOMIC
并删除!$OMP END CRITICAL
,则这两者之间的结果完全不同。在p1=p1+1
的这种情况下,这两个是不是可以互相替换?
我的想法: 1.最常见的问题可能是使用线程不安全的子程序。但我在下面的代码中找不到一个。
这是从我的代码中复制的一部分。
注意:
(i,j,k,k1,k2,k3,distance),因此不考虑由“PRIVATE”声明的私有变量的不确定性。
p1=0;
!$OMP PARALLEL PRIVATE(i,j,k,k1,k2,k3,distance)
!$OMP DO
do i=1,N_MESH;do j=1,N_MESH;do k=1,N_MESH;
!$OMP CRITICAL
p1=p1+1
!$OMP END CRITICAL
! - box-1. special treatment for doing specturm operations for FFT.
if (i.lt.(N_MESH/2+1))then
k1=i-1
elseif (i.eq.N_MESH/2+1) then
k1=0
else
k1=i-1-N_MESH
endif
if (j.lt.(N_MESH/2+1))then
k2=j-1
elseif (j.eq.N_MESH/2+1)then
k2=0
else
k2=j-1-N_MESH
endif
if (k.lt.(N_MESH/2+1))then
k3=k-1
elseif (k.eq.N_MESH/2+1)then
k3=0
else
k3=k-1-N_MESH
endif
! =============distance =====================
distance=(k1*k1)+(k2*k2)+(k3*k3);
! -----============put them into =======================
final_index(p1,l) = nint(dsqrt(distance));
if (((k1.eq.0).AND.(k2.eq.0)).AND.(k3.eq.0)) THEN
final(p1,l)=0d0
else
final(p1,l)=(abs(fu(i,j,k))**2+abs(fv(i,j,k))**2+abs(fw(i,j,k))**2)/2d0
endif
enddo;enddo;enddo
!$OMP END DO
!$OMP END PARALLEL
答案 0 :(得分:4)
问题是p1
是共享的,并且可能在循环的主体中发生变化。
假设您有两个线程,p1从零开始,并且它们同时启动。第一个等级0到达临界区,并将p1
增加到1,而等级1等待临界区结束。一旦等级0递增p1
,它就开始执行其余代码,但同时等级1开始执行临界区,并递增p1
。我们无法保证在final(p1,l) = ...
变为2之前,排名0将达到p1
语句。如果发生这种情况,final(1,l)
将永远不会更新。因此存在竞争条件。
为避免此问题,建议您根据p1
,i
和j
手动计算k
。这将让你p1
私密,保存你的关键部分,并消除这种竞争条件。
p1 = k + N_MESH*(j-1+N_MESH*(i-1))