带有子例程和函数的 Fortran OpenMP
Fortran OpenMP with subroutines and functions
免责声明:我很确定这已经在某个地方得到了回答,但我自己和另一个人一直在努力寻找但无济于事。
我有一个看起来像这样的代码:
PROGRAM main
!$omp parallel do
!$omp private(somestuff) shared(otherstuff)
DO i=1,n
...
CALL mysubroutine(args)
...
a=myfunction(moreargs)
...
ENDDO
!$omp end parallel do
END PROGRAM
SUBROUTINE mysubroutine(things)
...
END SUBROUTINE
FUNCTION myfunction(morethings)
...
END FUNCTION
我无法确定where/how 处理子例程和函数中变量的私有、共享、缩减等子句。我怀疑答案可能会有一些细微差别,因为有很多很多方法可以声明变量并在它们之间共享。因此,假设主程序涉及的所有变量都在其中或共享模块中定义,并且可以在主代码中处理对这些变量的任何 OMP 操作。子例程和函数使用其中一些变量,并有一些自己的变量。所以,我认为问题归结为如何处理局部变量的子句。
好的,这是关于 OpenMP 指令的词法和动态范围之间的差异以及与变量作用域的交互。指令的词法范围是指令后结构化块开始和结束之间的文本。动态范围是词法范围 plus 作为词法范围中的语句结果执行的任何子程序的一部分的语句。所以在类似
的情况下
Program stuff
Implicit None
Real, Dimension( 1:100 ) :: a
Call Random_number( a )
!$omp parallel default( none ) shared( a )
Call sub( a )
!$omp end parallel
Contains
Subroutine sub( a )
Real, Dimension( : ), Intent( InOut ) :: a
Integer :: i
!$omp do
Do i = 1, Size( a )
a( i ) = 2.0 * a( i )
End Do
End Subroutine Sub
End Program stuff
(完全未经测试,直接写在这里)由 !$omp parallel 启动的并行区域的词法范围只是
Call sub( a )
而动态范围是调用和子程序的内容。为了术语的完整性,!$omp do 是一个孤立指令的示例,该指令不在另一个指令的词法范围内,但在动态范围内。参见
https://computing.llnl.gov/tutorials/openMP/#Scoping
再举个例子
为什么这很重要?好吧,您只能在词法范围内为实体显式定义变量范围,在本例中仅为数组 a,但对于由于执行动态范围而定义的实体,您不能这样做!相反,OpenMP 有许多规则,简单来说就是
- 子程序参数的范围是从调用点继承的,即如果您在词法范围的开头将它们的范围限定为私有,它们将保持私有,如果共享它们将保持共享
- 子程序的局部变量默认是私有的(所以上面的 i 是私有的,如你所愿)除非它们是用 SAVE 属性声明的(显式或隐式),在这种情况下它们是共享的。
根据我的经验,这对您来说很重要!结合使用动态范围和孤立指令是控制 OpenMP 程序的好方法,我不得不说我不同意上面的评论,我发现孤立的工作共享指令确实非常有用!所以你可以结合以上所有的东西来做像
这样的事情
Program dot_test
Implicit None
Real, Dimension( 1:100 ) :: a, b
Real :: r
a = 1.0
b = 2.0
!$omp parallel default( none ) shared( a, b, r )
Call dot( a, b, r )
Write( *, * ) r
!$omp end parallel
Contains
Subroutine dot( a, b, r )
Real, Dimension( : ), Intent( In ) :: a, b
Real, Intent( Out ) :: r
Real, Save :: s
Integer :: i
!$omp single
s = 0.0
!$omp end single
!$omp do reduction( +:s )
Do i = 1, Size( a )
s = s + a( i ) * b( i )
End Do
!$omp end do
!$omp single
r = s
!$omp end single
End Subroutine dot
End Program dot_test
Wot now? gfortran -std=f95 -fopenmp -O -Wall -Wextra dot.f90
Wot now? export OMP_NUM_THREADS=3
Wot now? ./a.out
200.000000
200.000000
200.000000
这种简单的情况由于模块变量和公共块而变得有点复杂,所以不要使用全局变量……但如果必须的话,它们默认是共享的,除非声明为 threadprivate。参见
https://computing.llnl.gov/tutorials/openMP/#THREADPRIVATE
举个例子。
免责声明:我很确定这已经在某个地方得到了回答,但我自己和另一个人一直在努力寻找但无济于事。
我有一个看起来像这样的代码:
PROGRAM main
!$omp parallel do
!$omp private(somestuff) shared(otherstuff)
DO i=1,n
...
CALL mysubroutine(args)
...
a=myfunction(moreargs)
...
ENDDO
!$omp end parallel do
END PROGRAM
SUBROUTINE mysubroutine(things)
...
END SUBROUTINE
FUNCTION myfunction(morethings)
...
END FUNCTION
我无法确定where/how 处理子例程和函数中变量的私有、共享、缩减等子句。我怀疑答案可能会有一些细微差别,因为有很多很多方法可以声明变量并在它们之间共享。因此,假设主程序涉及的所有变量都在其中或共享模块中定义,并且可以在主代码中处理对这些变量的任何 OMP 操作。子例程和函数使用其中一些变量,并有一些自己的变量。所以,我认为问题归结为如何处理局部变量的子句。
好的,这是关于 OpenMP 指令的词法和动态范围之间的差异以及与变量作用域的交互。指令的词法范围是指令后结构化块开始和结束之间的文本。动态范围是词法范围 plus 作为词法范围中的语句结果执行的任何子程序的一部分的语句。所以在类似
的情况下Program stuff
Implicit None
Real, Dimension( 1:100 ) :: a
Call Random_number( a )
!$omp parallel default( none ) shared( a )
Call sub( a )
!$omp end parallel
Contains
Subroutine sub( a )
Real, Dimension( : ), Intent( InOut ) :: a
Integer :: i
!$omp do
Do i = 1, Size( a )
a( i ) = 2.0 * a( i )
End Do
End Subroutine Sub
End Program stuff
(完全未经测试,直接写在这里)由 !$omp parallel 启动的并行区域的词法范围只是
Call sub( a )
而动态范围是调用和子程序的内容。为了术语的完整性,!$omp do 是一个孤立指令的示例,该指令不在另一个指令的词法范围内,但在动态范围内。参见
https://computing.llnl.gov/tutorials/openMP/#Scoping
再举个例子
为什么这很重要?好吧,您只能在词法范围内为实体显式定义变量范围,在本例中仅为数组 a,但对于由于执行动态范围而定义的实体,您不能这样做!相反,OpenMP 有许多规则,简单来说就是
- 子程序参数的范围是从调用点继承的,即如果您在词法范围的开头将它们的范围限定为私有,它们将保持私有,如果共享它们将保持共享
- 子程序的局部变量默认是私有的(所以上面的 i 是私有的,如你所愿)除非它们是用 SAVE 属性声明的(显式或隐式),在这种情况下它们是共享的。
根据我的经验,这对您来说很重要!结合使用动态范围和孤立指令是控制 OpenMP 程序的好方法,我不得不说我不同意上面的评论,我发现孤立的工作共享指令确实非常有用!所以你可以结合以上所有的东西来做像
这样的事情Program dot_test
Implicit None
Real, Dimension( 1:100 ) :: a, b
Real :: r
a = 1.0
b = 2.0
!$omp parallel default( none ) shared( a, b, r )
Call dot( a, b, r )
Write( *, * ) r
!$omp end parallel
Contains
Subroutine dot( a, b, r )
Real, Dimension( : ), Intent( In ) :: a, b
Real, Intent( Out ) :: r
Real, Save :: s
Integer :: i
!$omp single
s = 0.0
!$omp end single
!$omp do reduction( +:s )
Do i = 1, Size( a )
s = s + a( i ) * b( i )
End Do
!$omp end do
!$omp single
r = s
!$omp end single
End Subroutine dot
End Program dot_test
Wot now? gfortran -std=f95 -fopenmp -O -Wall -Wextra dot.f90
Wot now? export OMP_NUM_THREADS=3
Wot now? ./a.out
200.000000
200.000000
200.000000
这种简单的情况由于模块变量和公共块而变得有点复杂,所以不要使用全局变量……但如果必须的话,它们默认是共享的,除非声明为 threadprivate。参见
https://computing.llnl.gov/tutorials/openMP/#THREADPRIVATE
举个例子。