0

在下面的代码中,当我将变量“ 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 ”时工作正常,但当我将它作为私有变量传递时却不行。提前感谢您的任何意见或建议。

4

2 回答 2

4

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

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

...
mulsd   __common_MOD_aa(%rip), %xmm2
...

内联代码也指aa模块中的全局值,即两个优化级别之间的行为是一致的。

这同样适用于英特尔 Fortran。

正确的做法是声明aathreadprivate把它放在一个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
...

Now both the parallel region and evolve will use a private to each thread copy of aa. As access to threadprivate variables is usually slower than access to normal private (stack) variables, on 64-bit x86 systems it might make more sense to pass the value of aa as an argument to evolve instead as suggested by @Bálint Aradi.

于 2013-03-15T16:52:42.620 回答
0

您应该尝试仔细分析您的变量,特别是考虑哪些变量会同时在不同线程上具有不同的值,因为这些变量必须声明为 OMP 私有。在您的情况下,两个变量都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)
于 2013-03-15T14:29:59.827 回答