在并行Fortran do循环中调用内部子例程时,子例程无法访问迭代变量的正确值

时间:2017-06-29 23:42:36

标签: parallel-processing fortran openmp gfortran subroutine

我试图编写一个Fortran程序,其中在并行do循环中调用内部子程序。因为子例程除了在这个循环中没有被调用,并且因为迭代变量 i 是全局的,所以我没有看到将它传递给子例程的需要。这是该程序的简化概述,突出了问题:

program test
  integer :: i
  i=37

  $omp parallel do private(i)
  do i=1,5
    call do_work
  enddo
  $omp end parallel do

contains
  subroutine do_work
    print *,i
  end subroutine do_work
end program test

我使用以下方法编译此程序:

gfortran -O0 -fopenmp -o test test.f90

我在一台8核的机器上使用gfortran 4.4.6编译它,在另一台8核的机器上使用gfortran 5.4.0,然后得到:

37
37
37
37
37

当然,在没有 -fopenmp 标志的情况下编译时,我得到了预期的输出:

1
2
3
4
5

所以似乎 i 的预循环值是do_work在每个线程中看到的。为什么子程序没有看到 i 的线程的本地值?为什么将 i 作为子例程的参数传递来解决问题?我对OpenMP很新,所以如果答案很明显,我会道歉。

2 个答案:

答案 0 :(得分:2)

OpenMP标准没有指定程序的行为。

如果您没有将i作为参数传递,并且您希望i对于构造中的每个线程都是私有的(物理上出现在并行和结束并行之间的源)指令)并且在区域内(在这些指令之间执行的源,那么您需要赋予i OpenMP threadprivate属性。

在过程do_work中,变量i由主机关联引用,并且在过程内部,它不会在OpenMP构造中出现词法 - 因此在过程内部,它是一个在区域但不在构造中。

通常,OpenMP 4.5的2.15.1.2规定在程序中对i的引用将被共享。

但是因为i是隐式的(因为它是一个do循环索引)并且在构造中显式私有,所以2.15.3.3声明未指定是否在区域中引用i而不是构造是原始(共享)项目或私有副本。

当您通过引用"传递i作为参数"时,伪参数具有与实际参数相同的数据共享属性 - 即,如果您将i传递给过程它变得私密。

答案 1 :(得分:0)

使用OpenMP,当您的程序进入do循环时,会创建一个“thread”。这与主程序调用的子程序类似,只是主程序的变量可用于子程序。

然后,由循环分隔的并行区域将创建私有变量的副本,以便每个线程都有自己的i版本。您的子例程只能看到“supervisor”程序的i,而不是线程的本地副本。当使用显式参数时,子例程将被明确告知使用i的“线程局部”值。

一般情况下(对于OpenMP),重要的是要仔细考虑并行区域的哪些变量是局部的,哪些变量可以保持“全局”。