我试图编写一个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很新,所以如果答案很明显,我会道歉。
答案 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),重要的是要仔细考虑并行区域的哪些变量是局部的,哪些变量可以保持“全局”。