带有子程序和函数的Fortran OpenMP

TTT*_*TTT 5 fortran openmp thread-safety

免责声明:我很确定这已在某个地方得到解答,但我和其他人一直在努力寻找无济于事.

我有一个看起来像这样的代码:

      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
Run Code Online (Sandbox Code Playgroud)

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

Ian*_*ush 9

好的,这是关于OpenMP指令的词法和动态范围与变量范围的交互之间的区别.指令的词法范围是指令后结构化块的开头和结尾之间的文本.动态范围是词汇范围加上作为由于词汇范围内的陈述而执行的任何子程序的一部分而执行的语句.所以在类似的东西

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
Run Code Online (Sandbox Code Playgroud)

(完全未经测试,直接写在这里)由$ $ omp parallel引发的并行区域的词汇范围就是

   Call sub( a )
Run Code Online (Sandbox Code Playgroud)

而动态范围是调用子例程的内容.为了完整的术语,!$ omp do是一个孤立指令的例子,这个指令不在另一个指令的词法范围内,而是在动态范围内.看到

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

再举一个例子.

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

  1. 子程序参数的范围是从调用点继承的,即如果你在词法范围的开头将它们限制为私有它们保持私有,如果共享它们保持共享
  2. 默认情况下,子程序的局部变量是私有的(因此我在上面是私有的,如你所愿),除非它们使用SAVE属性(显式或隐式)声明,在这种情况下它们是共享的.

根据我的经验,你可以获得最大的收益!使用动态范围与孤立指令相结合是一个很好的方法来控制OpenMP程序,我不得不说我不同意上面的评论,我发现孤立的workshare指令非常有用!所以你可以结合上面的所有内容去做

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  
Run Code Online (Sandbox Code Playgroud)

这个简单的情况因模块变量和公共块而有点复杂,所以不要使用全局变量......但如果必须它们默认共享,除非声明为threadprivate.看到

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

举个例子.