【问题标题】:Fortran OpenMP with subroutines and functions带有子例程和函数的 Fortran OpenMP
【发布时间】:2016-05-22 17:42:02
【问题描述】:

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

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

      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 操作都可以在主代码中处理。子例程和函数使用其中一些变量,并有一些自己的变量。所以,我认为问题归结为如何处理其局部变量的子句。

【问题讨论】:

  • 显示此类函数的示例。如果局部变量不是save,则不是问题。
  • stackoverflow.com/questions/22381795/…他们要研究的关键词是thread-safetythread-safe程序。
  • @VladimirF 澄清一下,我有其他两个的代码,所以它更多的是语法问题而不是线程安全。 SAVE 混合在一起,子例程和函数都被称为很多,因此避免不必要的重复内存分配很重要。但是,如果使用 SAVE 将它们撞到堆上,(并且会损害性能),那么这是不值得的。可能需要进行测试。
  • 我不明白你的评论。你的整个问题都是关于线程安全的,不是吗?你想到了什么样的语法?您不能访问不在范围内的变量。如果您有代码,请显示它!否则你的问题太宽泛了。
  • 我不确定您的评论,但是如果您在从 OpenMP 并行区域调用的过程中使用 SAVE 声明变量,您可能会在线程之间发生冲突。您可以在这些子例程中使用 OpenMP 指令:这种用法的术语是“孤立的”。

标签: fortran thread-safety openmp


【解决方案1】:

好的,这是关于 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 有许多规则,简单来说就是

  1. 子程序参数的范围是从调用点继承的,即,如果您在词法范围的开头将它们限定为私有,它们将保持私有,如果共享,它们将保持共享
  2. 默认情况下,子程序的局部变量是私有的(因此上面的 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

举个例子。

【讨论】:

  • 很好的例子,我错了。您显示的save 变量的处理也很有用。 Chandra 的 OpenMP 并行编程中有一个关于孤立指令的很好的讨论。
猜你喜欢
  • 2013-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-18
  • 2017-02-26
相关资源
最近更新 更多