Fortran OpenMP与子程序和函数 [英] Fortran OpenMP with subroutines and functions

查看:395
本文介绍了Fortran OpenMP与子程序和函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

免责声明:我确信这已经在某个地方得到了回答,但是我和另一个人一直在努力寻找无济于事。



我的代码看起来像这样:

  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

我无法确定子例程和函数中变量的位置/如何处理私有,共享,减少等子句。我怀疑答案可能会有一些细微差别,因为变量可能有很多种方法可能已经被声明和共享。所以,假设主程序所涉及的所有变量都在其中或共享模块中定义,并且可以在主代码中处理对这些变量的任何OMP操作。子程序和函数使用其中的一些变量,并有一些自己的变量。所以,我认为问题归结为如何处理其局部变量的子句。

解决方案

好的,这是关于OpenMP指令的词汇和动态范围与变量作用域之间的区别。指令的词汇范围是指令之后的结构化块的开始和结束之间的文本。动态范围是作为词汇范围内的语句结果执行的任何子程序的一部分执行的词汇范围 plus 语句。所以像

 程序员
隐式无
实数,维(1:100):: a
调用Random_number(a)
!$ omp并行默认(无)共享(a)
调用子(a)
!$ omp end parallel
包含
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启动的并行区域的词汇范围仅为

 呼叫子(a)

而动态范围是调用子程序的内容。为了完整的术语,!$ omp do是一个孤儿指令的例子,这个指令不在另一个指令的词汇范围内,而是在动态范围内。请参阅

https://计算。另一个例子是

这件事?那么你只能显式地为范围内的实体定义变量,在这种情况下只是数组a,但对于由于动态范围被执行而定义的实体,你不能这么做!相反,OpenMP有许多规则,简单地说就是


  1. 子程序参数的范围是从调用点继承的,也就是说,如果你在他们保持私有的词汇范围开始时将它们限定为私有的,并且如果共享它们保持共享

  2. 对子程序的本地变量默认是私有的(所以我在上面是私有的,因为除非用SAVE属性(显式或隐式)声明它们是共享的。

在我的体验,让你最大的方式!动态范围与孤儿指令结合使用是保持OpenMP程序受控制的好方法,我不得不说我不同意上述评论,我发现孤儿共享指令非常有用!因此,您可以将以上所有内容结合使用,如

 程序dot_test 
隐含无
真实, Dimension(1:100):: a,b
Real :: r
a = 1.0
b = 2.0
!$ omp并行默认(无)共享(a,b,r )
调用点(a,b,r)
写入(*,*)r
!$ omp end parallel
包含
子程序点(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
现在呢? gfortran -std = f95 -fopenmp -O -Wall -Wextra dot.f90
现在呢?导出OMP_NUM_THREADS = 3
现在呢? ./a.out
200.000000
200.000000
200.000000

简单的情况有点复杂的模块变量和公共块,所以不要使用全局变量...但如果你必须默认共享,除非声明为threadprivate。请参阅

https://计算。 llnl.gov/tutorials/openMP/#THREADPRIVATE



举例。


Disclaimer: I'm quite certain that this has been answered somewhere, but myself and another person have been searching quite hard to no avail.

I've got a code that looks something like this:

      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

I cannot determine where/how to handle private, shared, reduction, etc. clauses for the variables in the subroutine and function. I suspect there may be some nuances to the answer, as there are many, many ways variables might have been declared and shared amongst them. So, let's say all variables that the main program are concerned with were defined in it or in shared modules, and that any OMP operations on those variables can be handled in the main code. The subroutines and functions use some of those variables, and have some of their own variables. So, I think the question boils down to how to handle clauses for their local variables.

解决方案

OK, this is about the difference between the lexical and dynamic extent of OpenMP directives and the interaction with variable scoping. The lexical extent of a directive is the text between the beginning and the end of the structured block following a directive. The dynamic extent is the lexical extent plus statements executed as part of any subprogram executed as a result of statements in the lexical extent. So in something like

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

(totally untested, written direct in here) the lexical extent of the parallel region initiated by !$omp parallel is just

   Call sub( a )

while the dynamic extent is the call and the contents of the subroutine. And for completeness of terminology the !$omp do is an example of an orphaned directive, a directive that is not in the lexical extent of another directive, but in the dynamic extent. See

https://computing.llnl.gov/tutorials/openMP/#Scoping

for another example.

Why does this matter? Well you can only explicitly scope variables for entities in the lexical scope, just the array a in this case, but for entities that become defined due to the dynamic extent being executed you can't do this! Instead OpenMP has a number of rules, which in simple terms are

  1. The scope of subprogram arguments is inherited from the calling point, i.e. if you scoped them as private at the start of the lexical extent they stay private, and if shared they stay shared
  2. Local variables to subprograms are by default private (so i in the above is private, as you want) unless they are declared with the SAVE attribute (either explicitly or implicitly) in which case they are shared.

And in my experience that gets you most of the way! Use of the dynamic extent combined with orphaned directives is a great way to keep an OpenMP program under control, and I have to say I disagree with the above comment, I find orphaned workshare directives very useful indeed! So you can combine all of the above to do things like

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  

This simple situation is a bit complicated by module variables and common blocks, so don't use global variables ... but if you must they are by default shared unless declared as threadprivate. See

https://computing.llnl.gov/tutorials/openMP/#THREADPRIVATE

for an example.

这篇关于Fortran OpenMP与子程序和函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆