使用FORTRAN+;OpenMP
首先,我在不使用使用FORTRAN+;OpenMP,fortran,openmp,race-condition,Fortran,Openmp,Race Condition,首先,我在不使用-fopenmp的情况下编译了代码,并运行了代码,得到了一个串行结果,这是一个基准测试。 其次,我考虑使用OpenMP来加速我的代码 有两个奇怪的结果: 1.通过以下方式获得的结果: !$OMP CRITICAL p1=p1+1 !$OMP END CRITICAL 与系列结果有一点差异(1%)。我的代码不包含随机数,所以它一定是错的。 2.如果我替换$OMP临界带$OMP原子和删除$OMP END CRITICAL,两者之间的结果完全不同。在这种情
-fopenmp
的情况下编译了代码,并运行了代码,得到了一个串行结果,这是一个基准测试。
其次,我考虑使用OpenMP来加速我的代码
有两个奇怪的结果:
1.通过以下方式获得的结果:
!$OMP CRITICAL
p1=p1+1
!$OMP END CRITICAL
与系列结果有一点差异(1%)。我的代码不包含随机数,所以它一定是错的。
2.如果我替换$OMP临界
带$OMP原子
和删除$OMP END CRITICAL,两者之间的结果完全不同。在这种情况下,对于p1=p1+1
,这两者不是可以互相替换吗
我的想法:
1.最常见的问题可能是使用线程不安全的子例程。但我在下面的代码中找不到
这是从我的代码中复制的一部分
注意:
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
问题在于
p1
是共享的,并且在循环主体期间可能会发生变化
假设有两个线程,p1从零开始,它们同时开始。第一级0到达临界段,并将p1
增加到1,而第1级则等待临界段结束。一旦秩0完成递增p1
,它开始执行其余的代码,但同时秩1开始执行临界段,并递增p1
。在p1
变为2之前,不能保证秩0将到达最终(p1,l)=…
语句。如果发生这种情况,final(1,l)
将永远不会更新。所以有一个竞赛条件
p1 = k + N_MESH*(j-1+N_MESH*(i-1))
为了避免此问题,我建议您基于I
、j
和k
手动计算p1
。这将允许您将p1
设置为私有,为您保存一个关键部分,并删除此争用条件
p1 = k + N_MESH*(j-1+N_MESH*(i-1))
问题在于
p1
是共享的,并且在循环主体期间可能会发生变化
假设有两个线程,p1从零开始,它们同时开始。第一级0到达临界段,并将p1
增加到1,而第1级则等待临界段结束。一旦秩0完成递增p1
,它开始执行其余的代码,但同时秩1开始执行临界段,并递增p1
。在p1
变为2之前,不能保证秩0将到达最终(p1,l)=…
语句。如果发生这种情况,final(1,l)
将永远不会更新。所以有一个竞赛条件
p1 = k + N_MESH*(j-1+N_MESH*(i-1))
为了避免此问题,我建议您基于I
、j
和k
手动计算p1
。这将允许您将p1
设置为私有,为您保存一个关键部分,并删除此争用条件
p1 = k + N_MESH*(j-1+N_MESH*(i-1))
我有点赶时间,所以这可能是错的。我看不到任何保证,这条语句--
final_index(p1,l)=nint(dsqrt(distance))
--不会由具有相同lhs的多个线程执行<代码>最终索引,p1
和l
似乎都是共享的。事实上,我正在努力寻找l
在哪里得到任何价值。我有点匆忙,所以这可能是错误的。我看不到任何保证,这条语句--final_index(p1,l)=nint(dsqrt(distance))
--不会由具有相同lhs的多个线程执行<代码>最终索引,p1
和l
似乎都是共享的。事实上,我正在努力寻找l
在哪里有任何价值。答案很好!我真的没想到会发生这种事!我工作到凌晨4点。因此,综上所述,竞争条件不仅可能发生在多个线程同时读写(如a=a+1)中,而且也可能发生在多个位置读写,如在平行区域范围内的两个遥远位置。因为在逐行执行期间,可以更改共享变量。正如您所说,解决方案通常是寻找私有变量和共享变量之间的关系。@是的,每个线程都可以位于并行部分的任何位置—它们不会在同一时间执行同一行代码。在确定线程安全性时,必须考虑所有共享变量可以写入/读取的地方。我真的没想到会发生这种事!我工作到凌晨4点。因此,综上所述,竞争条件不仅可能发生在多个线程同时读写(如a=a+1)中,而且也可能发生在多个位置读写,如在平行区域范围内的两个遥远位置。因为在逐行执行期间,可以更改共享变量。正如您所说,解决方案通常是寻找私有变量和共享变量之间的关系。@是的,每个线程都可以位于并行部分的任何位置—它们不会在同一时间执行同一行代码。在确定线程安全性时,必须考虑所有共享变量可以写入/读取的地方。