在下面的代码中,当我将变量“ 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 ”时工作正常但不是当我将其作为私有变量传递时。提前感谢您的任何意见或建议。
答案 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私有。在您的情况下,变量aa
和iii
都必须是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