使用FORTRAN + OpenMP进行do循环的奇怪结果

时间:2015-07-01 07:12:41

标签: fortran openmp race-condition

首先我编译了没有-fopenmp的代码,然后运行代码,获得了一个基准的串行结果。 其次我考虑使用OpenMP加速我的代码。

有两个奇怪的结果: 1.结果

   !$OMP CRITICAL
       p1=p1+1
   !$OMP END CRITICAL 

与序列结果略有差异(1%)。我的代码不包含随机数,所以它一定是错的。 2.如果我将!$OMP CRITICAL替换为!$OMP ATOMIC并删除!$OMP END CRITICAL,则这两者之间的结果完全不同。在p1=p1+1的这种情况下,这两个是不是可以互相替换?

我的想法: 1.最常见的问题可能是使用线程不安全的子程序。但我在下面的代码中找不到一个。

这是从我的代码中复制的一部分。

注意:

  1. i,j,k,k1,k2,k3以外的变量,距离被认为是“共享”
  2. 下面的代码是我在整个代码中并行化的唯一部分。
  3. 在并行循环之后不使用
  4. (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
    

1 个答案:

答案 0 :(得分:4)

问题是p1是共享的,并且可能在循环的主体中发生变化。

假设您有两个线程,p1从零开始,并且它们同时启动。第一个等级0到达临界区,并将p1增加到1,而等级1等待临界区结束。一旦等级0递增p1,它就开始执行其余代码,但同时等级1开始执行临界区,并递增p1。我们无法保证在final(p1,l) = ...变为2之前,排名0将达到p1语句。如果发生这种情况,final(1,l)将永远不会更新。因此存在竞争条件。

为避免此问题,建议您根据p1ij手动计算k。这将让你p1私密,保存你的关键部分,并消除这种竞争条件。

p1 = k + N_MESH*(j-1+N_MESH*(i-1))