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

Weird result for a do loop with FORTRAN+OpenMP

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

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

获得的结果
   !$OMP CRITICAL
       p1=p1+1
   !$OMP END CRITICAL 

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

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

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

注意:

  1. 除了 i,j,k,k1,k2,k3,distance 之外的变量被认为是 "SHARED"
  2. 下面的代码是我在整个代码中并行化的唯一部分。
  3. (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
    

问题是 p1 是共享的,可能会在循环主体期间发生变化。

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

为避免此问题,我建议您根据ijk 手动计算p1。这将使您可以将 p1 设为私有,为您节省关键部分,并消除此竞争条件。

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