带有子例程和函数的 Fortran OpenMP [英] Fortran OpenMP with subroutines and functions

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

问题描述

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

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

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

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.

推荐答案

好的,这是关于 OpenMP 指令的词法和动态范围以及与变量作用域的交互之间的区别.指令的词汇范围是指令后结构化块的开头和结尾之间的文本.动态范围是作为任何子程序的一部分执行的词法范围plus语句,该子程序是作为词法范围中的语句的结果而执行的.所以像

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

(完全未经测试,直接写在这里)!$omp parallel 发起的并行区域的词法范围只是

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

   Call sub( a )

而动态范围是调用子程序的内容.为了术语的完整性,!$omp 是孤立指令的一个示例,该指令不在另一个指令的词汇范围内,而是在动态范围内.见

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

再举一个例子.

为什么这很重要?好吧,您只能为词法范围内的实体显式范围变量,在这种情况下只是数组 a ,但是对于由于正在执行的动态范围而被定义的实体,您不能这样做!相反,OpenMP 有许多规则,简单来说就是

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. 子程序参数的范围是从调用点继承的,即如果您在词法范围的开头将它们限定为私有,它们保持私有,如果共享,它们保持共享
  2. 默认情况下,子程序的局部变量是私有的(所以上面的 i 是私有的,如您所愿),除非它们使用 SAVE 属性(显式或隐式)声明,在这种情况下它们是共享的.

根据我的经验,这可以让您获得最大的收益!将动态范围与孤立指令结合使用是控制 OpenMP 程序的好方法,我不得不说我不同意上述评论,我发现孤立的工作共享指令确实非常有用!所以你可以结合以上所有的事情来做类似的事情

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  

这种简单的情况由于模块变量和公共块有点复杂,所以不要使用全局变量……但如果必须,它们默认是共享的,除非声明为 threadprivate.见

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

举个例子.

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

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