我认为我的问题与here所描述的问题有关甚至相同。但我不明白实际发生了什么。
我正在使用带有gfortran编译器的openMP,我有以下任务要做:我在二维表面上有一个密度分布F(X, Y)
,x坐标X
和y坐标Y
。矩阵F
的大小为Nx
x Ny
。
我现在有一组坐标Xp(i)
和Yp(i)
,我需要在这些点上插入密度F
。此问题是为了并行化。
!$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i)
do i=1, Nmax
! Some stuff to be done here
Fint(i) = interp2d(Xp(i), Yp(i), X, Y, F, Nx, Ny)
! Some other stuff to be done here
end do
!$OMP END PARALLEL DO
除了i
之外,所有内容都是共享的。函数interp2d
正在进行一些简单的线性插值。
一个线程可以正常工作但多线程失败。我将问题追溯到hunt
- 从Numerical Recipes中获取的子程序,该程序由interp2d
调用。 hunt
- 子例程基本上计算索引ix
,使X(ix) <= Xp(i) < X(ix+1)
。这是获得插值的起点所必需的。
使用多线程时,它会偶尔发生,一个线程从ix
获取正确的索引hunt
,并且调用hunt
的线程接下来获得完全相同的索引,即使Xp(i)
甚至不接近那一点。
我可以使用CRITICAL
环境阻止这种情况:
!$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(i)
do i=1, Nmax
! Some stuff to be done here
!$OMP CRITICAL
Fint(i) = interp2d(Xp(i), Yp(i), X, Y, F, Nx, Ny)
!$OMP END CRITICAL
! Some other stuff to be done here
end do
!$OMP END PARALLEL DO
但这会降低效率。如果我使用例如三个线程,我的CRITICAL
环境的平均负载为1.5。没有我的负载平均值为2.75,但结果错误,甚至有时会出现SIGSEGV
运行时错误。
这到底发生了什么?在我看来,所有线程都在调用相同的hunt
- 子例程,如果它们同时执行,则会发生冲突。这有意义吗?
我该如何防止这种情况?
答案 0 :(得分:1)
在Fortran 90+中组合变量声明和初始化具有赋予变量SAVE
属性的副作用。
integer :: i = 0
大致相当于:
integer, save :: i
if (first_invocation) then
i = 0
end if
SAVE
'变量在例程的多次调用之间保留其值,因此通常作为静态变量实现。根据管理OpenMP中隐式数据共享类的规则,除非在threadprivate
指令中列出,否则这些变量将被共享。
OpenMP要求兼容编译器应该应用上述语义,即使基础语言是Fortran 77也是如此。