私有的OpenMP FORTRAN问题

时间:2013-03-15 12:44:42

标签: openmp fortran90

在下面的代码中,当我将变量“ aa ”作为私有传递时,结果变得很糟糕。代码工作正常如何发布,但当我替换行

   !$OMP PARALLEL PRIVATE(iii,iter,y,i,yt) SHARED(bb)

   !$OMP PARALLEL PRIVATE(aa,iter,y,i,yt) SHARED(bb)

代码无法正常运行。

     !!!!!!!! module 
      module common
      use iso_fortran_env
      implicit none
      integer,parameter:: dp=real64
      real(dp):: aa,bb

       contains

      subroutine evolve(y,yevl)
      implicit none
      integer(dp),parameter:: id=2
      real(dp),intent(in):: y(id)
      real(dp),intent(out):: yevl(id)
        yevl(1)=y(2)+1.d0-aa*y(1)**2
        yevl(2)=bb*y(1)
      end subroutine evolve

      end module common

      use common
      implicit none
      integer(dp):: iii,iter,i
      integer(dp),parameter:: id=2
      real(dp),allocatable:: y(:),yt(:)
      integer(dp):: OMP_GET_THREAD_NUM, IXD

       allocate(y(id)); allocate(yt(id)); y=0.d0; yt=0.d0; bb=0.3d0
       !$OMP PARALLEL PRIVATE(iii,iter,y,i,yt) SHARED(bb)
         IXD=OMP_GET_THREAD_NUM()
       !$OMP DO
        do iii=1,20000; print*,iii  !! EXPECTED THREADS TO BE OF 5000 ITERATIONS EACH
          aa=1.d0+dfloat(iii-1)*0.4d0/2000.d0
            loop1: do iter=1,10 !! THE INITIAL CONDITION LOOP
               call random_number(y)!! RANDOM INITIALIZATION OF THE VARIABLE
                loop2: do i=1,70000  !! ITERATION OF THE SYSTEM
                    call evolve(y,yt)
                    y=yt
                enddo loop2     !! END OF SYSTEM ITERATION
              write(IXD+1,*)aa,yt  !!! WRITING FILE CORRESPONDING TO EACH THREAD
            enddo loop1 !!INITIAL CONDITION ITERATION DONE
         enddo
        !$OMP ENDDO
        !$OMP END PARALLEL
        end

可能是什么问题?当我从“ iii ”生成“ aa ”时工作正常但不是当我将其作为私有变量传递时。提前感谢您的任何意见或建议。

2 个答案:

答案 0 :(得分:4)

aa是一个模块变量。模块变量可以共享(默认)或threadprivate。 OpenMP标准文档中的示例A.32.2f说明了在构造的动态范围内访问模块变量时,未指定是否正在访问原始变量或专用线程副本threadprivate变量不是这种情况,因为它们总是存储在线程局部存储中,无论是否在并行区域的词法范围内使用。

如果将模块变量声明为私有,然后将其访问到子例程中,则会出现许多情况。最可能发生的事情取决于编译器对代码执行何种分析。一些编译器可能会检测到,模块子例程仅在并行区域内调用,因此使aa引用每个线程的私有副本。其他编译器可能决定始终访问原始模块变量。另一方面,如果子例程在调用子例程中被内联,则它可能引用在调用上下文中使用的相同aa(例如,如果aa被声明,则为私有版本{{1} }})

以下是private在默认优化级别处理gfortran的示例:

PRIVATE(iii,aa,iter,y,i,yt)

私有; aa is declared as a global symbol in the BSS section .globl __common_MOD_aa .bss .align 8 .type __common_MOD_aa, @object .size __common_MOD_aa, 8 __common_MOD_aa: .zero 8 ; Here is how evolve accesses aa ... movsd __common_MOD_aa(%rip), %xmm2 ... ; Here is how the assignment to aa is done inside the parallel region ... movsd %xmm0, -72(%rbp) ... 实现为自动变量并存储在线程的堆栈中,而aa使用模块中evolve的值。因此这个运营商:

aa

仅改变线程内{​​{1}}的值,而aa=1.d0+dfloat(iii-1)*0.4d0/2000.d0 使用并行区域外的aa原始值。

在高优化级别evolve aa-O3内联到并行区域中......

gfortran

内联代码还引用模块中evolve的全局值,即两个优化级别之间的行为是一致的。

这同样适用于英特尔Fortran。

正确的方法是将... mulsd __common_MOD_aa(%rip), %xmm2 ... 声明为aa,将声明为aa子句:

threadprivate

现在并行区域和private都将对module common use iso_fortran_env implicit none integer,parameter:: dp=real64 real(dp):: aa,bb !$OMP THREADPRIVATE(aa) ... !$OMP PARALLEL PRIVATE(iii,iter,y,i,yt) SHARED(bb) IXD=OMP_GET_THREAD_NUM() !$OMP DO do iii=1,20000; print*,iii !! EXPECTED THREADS TO BE OF 5000 ITERATIONS EACH aa=1.d0+dfloat(iii-1)*0.4d0/2000.d0 ... 的每个线程副本使用私有。由于对threadprivate变量的访问通常比访问普通私有(堆栈)变量更慢,因此在64位x86系统上将evolve的值作为参数传递给aa可能更有意义。由@BálintAradi建议。

答案 1 :(得分:0)

您应该尝试仔细分析您的变量,尤其是考虑哪些变量在不同的线程上同时具有不同的值,因为必须将它们声明为OMP私有。在您的情况下,变量aaiii都必须是OMP私有的。变量iii因为它是循环中的一个计数器,它分布在线程上,aa因为它得到一个取决于iii的值。

编辑:当每个线程调用evolve子例程本身并且evolve应该使用线程特定值aa(我猜)时,你也应该传递{{1}转到你的子程序而不是使用模块变量aa

例程应该如下:

aa

以及主程序中的相应调用:

subroutine evolve(y, aa, yevl)
  integer(dp),parameter:: id=2
  real(dp),intent(in):: y(id), aa
  real(dp),intent(out):: yevl(id)
    yevl(1)=y(2)+1.d0-aa*y(1)**2
    yevl(2)=bb*y(1)
  end subroutine evolve