在下面的代码中,当我将变量"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”时工作得很好,但是当我把它作为私有变量传递时就不行了。谢谢您的意见或建议。
发布于 2013-03-15 14:29:59
您应该仔细分析变量,特别是考虑它们在不同线程上同时具有不同的值,因为这些值必须声明为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-15 16:52:42
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将evolve内联到并行区域,并且.
...
mulsd __common_MOD_aa(%rip), %xmm2
...内联代码还引用模块中aa的全局值,即两个优化级别之间的行为是一致的。
英特尔Fortran的情况也是如此。
正确的方法是将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
...现在,并行区域和evolve都将对aa的每个线程副本使用一个私有的。由于对线程私有变量的访问通常比对普通私有(堆栈)变量的访问慢,因此在64位x86系统上将aa的值作为参数传递给evolve可能更有意义,正如@Bálint所建议的那样。
https://stackoverflow.com/questions/15432853
复制相似问题