OpenMP FORTRAN与私有问题
在下面的代码中,当我将变量“aa”作为private传递时,结果变得很糟糕。代码在发布时运行良好,但当我替换行时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
!$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