带有子例程和函数的 Fortran OpenMP

标签 fortran thread-safety 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

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

最佳答案

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

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 是孤立指令的一个示例,该指令不在另一个指令的词汇范围内,而是在动态范围内。看

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

再举一个例子。

为什么这很重要?好吧,您只能为词法范围内的实体显式范围变量,在这种情况下只是数组 a ,但是对于由于正在执行的动态范围而被定义的实体,您不能这样做!相反,OpenMP 有许多规则,简单来说就是
  • 子程序参数的范围是从调用点继承的,即如果您在词法范围的开头将它们限定为私有(private),它们保持私有(private),如果共享,它们保持共享
  • 子程序的局部变量默认是私有(private)的(所以上面的 i 是私有(private)的,如你所愿),除非它们使用 SAVE 属性(显式或隐式)声明,在这种情况下它们是共享的。

  • 根据我的经验,这可以让您获得最大的 yield !将动态范围与孤立指令结合使用是控制 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  
    

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

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

    例如。

    关于带有子例程和函数的 Fortran OpenMP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35347944/

    相关文章:

    fortran - 最终程序的问题(gfortran 的段错误)

    Python 包设置 : setup. py,自定义处理包装的 Fortran

    multithreading - 基于静态状态的回收与基于时代的回收

    c - POSIX 扩展可重入定义,涵盖线程安全

    concurrency - 有没有一种简单的方法可以使用 Fortran 中的随机数生成器进行并发计算?

    segmentation-fault - Fortran 奇怪的赋值,是 4294967295 == .true.?

    java - ConcurrentSkipListMap put 方法是线程安全的吗?

    c - C代码中使用OpenMP后输出错误

    c++ - 具有限制指针的 OpenMP 因 ICC 而失败,而 GCC/G++ 成功

    c++ - 段错误 openmp 错误