OpenMP FORTRAN与私有问题

OpenMP FORTRAN与私有问题,openmp,fortran90,Openmp,Fortran90,在下面的代码中,当我将变量“aa”作为private传递时,结果变得很糟糕。代码在发布时运行良好,但当我替换行时 !$OMP PARALLEL PRIVATE(iii,iter,y,i,yt) SHARED(bb) 与 代码工作不正常 !!!!!!!! module module common use iso_fortran_env implicit none integer,parameter:: dp=real64

在下面的代码中,当我将变量“aa”作为private传递时,结果变得很糟糕。代码在发布时运行良好,但当我替换行时

   !$OMP PARALLEL PRIVATE(iii,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”时效果很好,但当我将其作为私有变量传递时效果不好。提前感谢您的任何意见或建议

您应该仔细分析您的变量,尤其是考虑哪些变量在不同线程上同时具有不同的值,因为这些变量必须声明为OMP private。在您的情况下,变量
aa
iii
都必须是OMP专用的。变量
iii
,因为它是分布在线程上的循环中的计数器;
aa
,因为它获取的值取决于
iii

编辑:由于每个线程都调用
evolve
子例程本身,并且
evolve
应该使用线程特定的值
aa
(我猜),因此还应该将
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
以及主程序中相应的调用:

call evolve(y, aa, yt)

您应该仔细分析您的变量,特别是考虑哪些变量在不同的线程上同时具有不同的值,因为这些变量必须声明为OMP private。在您的情况下,变量
aa
iii
都必须是OMP专用的。变量
iii
,因为它是分布在线程上的循环中的计数器;
aa
,因为它获取的值取决于
iii

编辑:由于每个线程都调用
evolve
子例程本身,并且
evolve
应该使用线程特定的值
aa
(我猜),因此还应该将
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
以及主程序中相应的调用:

call evolve(y, aa, yt)

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

如果您将模块变量声明为私有,然后将其访问到子例程中,会发生许多情况。最可能发生的情况取决于编译器对代码所做的分析。一些编译器可能检测到模块子例程仅在并行区域内调用,因此使
aa
引用每个线程的私有副本。其他编译器可能决定始终访问原始模块变量。另一方面,如果子例程内联到调用子例程中,则它可能引用调用上下文中使用的相同
aa
(例如,如果
aa
声明为
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
值。因此,该操作员:

aa=1.d0+dfloat(iii-1)*0.4d0/2000.d0
仅在线程内部更改
aa
的值,而
evolve
从并行区域外部使用
aa
的原始值

在高优化水平下,
-O3
gfortran
内联线
演变成并行区域并

...
mulsd   __common_MOD_aa(%rip), %xmm2
...
内联代码还指模块中
aa
的全局值,即两个优化级别之间的行为是一致的

英特尔Fortran也是如此

正确的方法是将
aa
声明为
threadprivate
,并not将其放入
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
...

现在,并行区域和
evolve
将使用
aa
的每个线程的私有副本。由于对threadprivate变量的访问通常比对普通私有(堆栈)变量的访问慢,因此在64位x86系统上,将
aa
的值作为参数传递给
evolve
,而不是@Bálint Aradi所建议的那样。

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

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