当我使用OpenMP运行并行程序时,gfortran编译器给出了错误的答案。同时,ifort给出准确的结果。

这是整个可编译代码。

!_______________________________________________________________ !
!____________MODULE SECTION_____________________________________ !

  MODULE MATRIC
    IMPLICIT NONE
    INTEGER , PARAMETER :: NG = 40
    DOUBLE PRECISION,SAVE :: Z , PA , PB ,CMU
    DOUBLE PRECISION , PARAMETER :: PI=2.0D0*ACOS(0.0D0) , &
             FPI=4.0D0*PI , SQFPI = SQRT(FPI), DLAM=1.0D0
    DOUBLE PRECISION , DIMENSION(450) :: DEL1,  DEL2, X,  R ,  SNLO
    DOUBLE PRECISION :: XG(60) , WG(60)
  END MODULE MATRIC
!_________________________________________________________________________!
!                  MODULE SECTION
!__________________________________________________________________________!

  MODULE POTDATA
    IMPLICIT NONE
    INTEGER                            :: IA , IB , ID
    DOUBLE PRECISION                   :: RA , RB , R1s(450)
  END MODULE POTDATA
!__________________________________________________________________________!



!______________________________________________________________________!

  program check
    use matric
    use potdata
    implicit double precision(a-h,o-z)

    pa   = 0.72D0  ;  pb   =  0.19D0
    mesh = 441     ;  noint=  40      ;  z   =  2.0d0
    CALL GAULEG(-1.d0,1.d0)

    NB = MESH/NOINT
    I = 1
    X(I) = 0.0D+00
    DELTAX = 0.0025D+00*40.0D+00/DBLE(NOINT)
    DO  J=1,NB
      IMK = (J-1)*NOINT + 1
      DO K=1,NOINT
        AK=K
        I=I+1
        X(I)=X(IMK)+AK*DELTAX
      END DO
      DELTAX=2.0D+00*DELTAX
    END DO

    CMU=(9.0D00*PI*PI/(128.0D00*Z))**THIRD

    R(1)=0.0D+00 ;  SNLO(1) = 0.D00
    DO  I=2,MESH
      R(I)=CMU*X(I)
      SNLO(I) = R(I)*dexp(-Z*R(I))
      R1S(I) = SNLO(I)/(SQFPI*R(I))
    END DO

    call EFFPOT(MESH,NOINT)

  end program check


  subroutine EFFPOT(MESH,NOINT)
    USE OMP_LIB
    USE MATRIC
    USE POTDATA
    implicit none
    integer, intent(in) :: MESH, NOINT
    double precision::anorm(450)
    double precision, external :: funct
    double precision :: asum, fac, cnorm

!$omp parallel do default(none) private(del1,ia,asum,ib,ra,rb,fac) &
!$omp shared(id,mesh,r,anorm,NOINT,del2,R1s)
    do  ia = 2,mesh
      ra = r(ia)
      if(R1s(ia).lt.1.D-7.and.R1s(ia).ge.1.D-8)id = ia
      do ib = 2,mesh
         rb = r(ib)
         call QGAUSS(funct,-1.d0,1.d0,fac)
         del1(ib) = rb**2*fac*R1s(ib)**2
      end do
      CALL NCDF(del1,ASUM,r(2),mesh,NOINT)
      anorm(ia) = 2.0d0*pi*asum
      del2(ia)  = 2.0d0*pi*asum*(ra*R1s(ia))**2
    end do
!$omp end parallel do

    CALL NCDF(del2,ASUM,r(2),mesh,NOINT)
    cnorm = 1.0/dsqrt(4.*pi*ASUM)
    write(6,*)'cnorm =',cnorm

    return
  end


  double precision function funct(x)

    USE POTDATA , ONLY : RA , RB
    USE MATRIC  , ONLY : PA , PB  , DLAM

    implicit none
    double precision, intent(in) :: x
    double precision             :: f1, f2, ramrb

    ramrb = dsqrt(ra**2+rb**2-2.d0*ra*rb*x)
    f1 = dcosh(pa*ra)+dcosh(pa*rb)
    f2  = 1.d0+0.5*dlam*ramrb*dexp(-pb*ramrb)
    funct = (f1*f2)**2
    return
  end


  SUBROUTINE QGAUSS(func,aa,bb,ss)
    USE OMP_LIB
    USE MATRIC , ONLY : XG ,WG , NG
    IMPLICIT DOUBLE PRECISION(A-H,O-Z)
    external func
    xm = 0.5d0*(bb+aa)
    xl = 0.5d0*(bb-aa)
    ss = 0.d0
    do  j=1,ng
      dx = xl*xg(j)
      ss = ss + wg(j)*(func(xm+dx)+func(xm-dx))
    end do
    ss = xl*ss/2.0
    return
  END


  SUBROUTINE GAULEG(x1,x2)

    USE MATRIC , ONLY : XG ,WG ,NG , PI

    IMPLICIT DOUBLE PRECISION(A-H,O-Z)
    eps = 1.d-14
    m = (ng+1)/2
    xm = 0.5D0*(x1+x2)
    xl = 0.5D0*(x2-x1)

    do i=1,m
      z = dcos(pi*(dfloat(i)-0.25d0)/(dfloat(ng)+0.5d0))
1     continue
      p1 = 1.d0
      p2 = 0.d0

      do j=1,ng
        p3 = p2
        p2 = p1
        p1 = ((2.d0*dfloat(j)-1.d0)*z*p2  &
          - (dfloat(j)-1.d0)*p3)/dfloat(j)
      end do

      pp = dfloat(ng)*(z*p1-p2)/(z*z-1.d0)
      z1 = z
      z = z1 - p1/pp
      if (dabs(z-z1).gt.eps) go to 1
      xg(i) = xm - xl*z
      xg(ng+1-i) = xm + xl*z
      wg(i) = 2.d0*xl/((1.d0-z*z)*pp*pp)
      wg(ng+1-i) = wg(i)
    end do
    return
  end


  SUBROUTINE NCDF(F,ASUM,H,KKK,NOINT)
    IMPLICIT DOUBLE PRECISION (A-H,O-Z)
    DIMENSION F(450)
    NBLOCK = (KKK-2)/NOINT + 1
    C2HO45 = 2.0D+00*H/45.0D+00
    ASUM = 0.0D+00

    DO  J=1,NBLOCK
      ISTAR = NOINT*(J-1)+5
      IEND = NOINT*J + 1
      IEND = MIN0(KKK,IEND)
      DO  I=ISTAR,IEND,4
          ASUM = ASUM + C2HO45*(7.0D+00*(F(I-4)+F(I))  &
                +32.0D+00*(F(I-3)+F(I-1)) + 12.0D+00*F(I-2))
      END DO
      IF(IEND.EQ.KKK) GO TO 4
      C2HO45 = 2.0D+00*C2HO45
4   END DO

    RETURN
  END


特别感谢对我的问题感兴趣的所有人@Vladimir。最后,通过从模块potdata中删除ra和rb并将函数定义为funct(x,ra,rb),然后从循环中删除ra和rb,我得到了正确的答案。因为我正在写ra,rb然后在上面的代码中读取它们的值,所以循环具有流依赖性。现在,我从两个编译器(均为8.7933767516)并行,依次两个地获得准确的结果。确切的方法是这样

subroutine EFFPOT(MESH,NOINT)
    USE OMP_LIB
    USE MATRIC
    USE POTDATA
  implicit none
  integer, intent(in) :: MESH, NOINT
  double precision::anorm(450)
  double precision, external :: funct
  double precision :: asum, fac, cnorm
 !$omp parallel do default(none) private(del1,ia,asum,ib,fac) &
 !$omp shared(id,mesh,r,anorm,NOINT,del2,R1s)

  do  ia = 2,mesh
      if(R1s(ia).lt.1.D-7.and.R1s(ia).ge.1.D-8)id = ia
      do ib = 2,mesh
         call QGAUSS(funct,-1.d0,1.d0,fac,r(ia),r(ib))
         del1(ib) = r(ib)**2*fac*R1s(ib)**2
      end do
      CALL NCDF(del1,ASUM,r(2),mesh,NOINT)
      anorm(ia) = 2.0d0*pi*asum
      del2(ia)  = 2.0d0*pi*asum*(r(ia)*R1s(ia))**2
  end do

 !$omp end parallel do
  CALL NCDF(del2,ASUM,r(2),mesh,NOINT)
  cnorm = 1.0/dsqrt(4.*pi*ASUM)
  write(6,*)'cnorm =',cnorm

  return
  end


      double precision function funct(x,ra,rb)
      USE MATRIC  , ONLY : PA , PB  , DLAM

      implicit none
      double precision, intent(in) :: x, ra, rb
      double precision             :: f1, f2, ramrb

      ramrb = dsqrt(ra**2+rb**2-2.d0*ra*rb*x)
      f1 = dcosh(pa*ra)+dcosh(pa*rb)
      f2  = 1.d0+0.5*dlam*ramrb*dexp(-pb*ramrb)
      funct = (f1*f2)**2
  return
  end
  SUBROUTINE QGAUSS(func,aa,bb,ss,ra,rb)
     USE OMP_LIB
     USE MATRIC , ONLY : XG ,WG , NG
     IMPLICIT DOUBLE PRECISION(A-H,O-Z)
     external func
     xm = 0.5d0*(bb+aa)
     xl = 0.5d0*(bb-aa)
     ss = 0.d0
   do  j=1,ng
     dx = xl*xg(j)
     ss = ss + wg(j)*(func(xm+dx,ra,rb)+func(xm-dx,ra,rb))
   end do
   ss = xl*ss/2.0
   return
  END

最佳答案

问题的原因在于,如果在区域中但在构造外部访问了private列表项,则OpenMP标准未指定会发生什么。有关相同问题的简短版本,请参见示例private.2f(位于OpenMP标准补充资料的第135页)。

具体来说,模块变量rarbprivate内的OpenMP并行区域中声明为EFFPOT,也可以从funct访问。 funct处于并行区域的动态范围内,但(在词法上)位于并行区域之外,因此未指定ra引用的rbfunct是原始模块变量还是它们的私有副本(大多数编译器)将用于原始变量)。

您已经找到一种解决方案。另一个将是声明rarb threadprivate,因为它们仅用于将数据从EFFPOT传递到funct

MODULE POTDATA
  IMPLICIT NONE
  INTEGER                            :: IA , IB , ID
  DOUBLE PRECISION                   :: RA , RB , R1s(450)
  !$OMP THREADPRIVATE(RA,RB)
END MODULE POTDATA


然后,您还应该从ra内并行区域的rb子句的列表中删除privateEFFPOT

在某些平台上,例如由于模拟了TLS,因此将threadprivate与GCC(即gfortran)一起使用的OS X可能比实际传递两个变量作为参数要慢。

请注意,这种语义错误确实很难检测到,许多OpenMP工具实际上不会发现它。

关于fortran - 并行运行代码时结果错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30669169/

10-11 22:58