我对fortran中关于openmp的这个问题非常困惑。具体来说,当我编写这样的程序时:
PROGRAM TEST
IMPLICIT NONE
INTEGER :: i,j,l
INTEGER :: M(2,2)
i=2
j=2
l=41
!$OMP PARALLEL SHARED(M),PRIVATE(l,i,j)
!$OMP DO
DO i=1,2
DO j=1,2
DO l=0,41
M(i,j)=M(i,j)+1
ENDDO
ENDDO
ENDDO
!$OMP END DO
!$OMP END PARALLEL
END PROGRAM TEST
按照ifort -openmp test.f90
进行编译后,效果很好,M(1,1)
的结果与预期的结果为42。
但是,当我仅调整总和超过l
和{i,j}
的顺序时,如下所示:
PROGRAM TEST
IMPLICIT NONE
INTEGER :: i,j,l
INTEGER :: M(2,2)
i=2
j=2
l=41
!$OMP PARALLEL SHARED(M),PRIVATE(l,i,j)
!$OMP DO
DO l=0,41
DO i=1,2
DO j=1,2
M(i,j)=M(i,j)+1
ENDDO
ENDDO
ENDDO
!$OMP END DO
!$OMP END PARALLEL
END PROGRAM TEST
编译后:ifort -openmp test.f90
,效果不佳。事实上,当您多次运行a.out时,M(1,1)
的结果似乎是随机的。有谁知道这是什么问题?另外,如果我想在求和顺序下获得正确的结果:
DO l=0,41
DO i=1,2
DO j=1,2
我应该在哪个部分修改此代码?
非常感谢您的帮助。
答案 0 :(得分:2)
你有竞争条件。具有不同l
的线程正在尝试使用相同的元素M(i,j)
。您可以使用Intel Inspector或Oracle Thread Analyzer等工具查找它(我已与英特尔核实)。最好的办法是使用原始订单。您也可以使用简化,但要小心更大的数组:
PROGRAM TEST
IMPLICIT NONE
INTEGER :: i,j,l
INTEGER :: M(2,2)
M = 0
!$OMP PARALLEL DO PRIVATE(l,i,j),reduction(+:M)
DO l = 0, 41
DO i = 1, 2
DO j = 1, 2
M(i,j) = M(i,j) + 1
END DO
END DO
END DO
!$OMP END PARALLEL DO
print *, M
END PROGRAM
答案 1 :(得分:1)
您的方法存在许多问题。首先,缺少数组的初始化M.在循环中,你发出
M(i,j) = M(i,j) + 1
没有给M(i,j)
提供任何初始值。因此,即使在串行情况下,算法也是不确定的,只是缺少一个问题,即使用任何特定的编译器或任何特定的求和顺序获得正确的结果。
另外,如果您将循环并行化为l
,例如
!$OMP PARALLEL DO SHARED(M),PRIVATE(l,i,j)
DO l = 0, 41
DO i = 1, 2
DO j = 1, 2
M(i,j) = M(i,j) + 1
END DO
END DO
END DO
每个线程都有一个自己的嵌套循环结构i
和j
覆盖所有矩阵元素。因此,不同的线程将同时访问矩阵的相同元素。结果再次是不确定的。当然,您可以尝试通过确保通过OpenMP构造解决问题,线程在访问某个矩阵元素之前相互等待。但是,这会使算法肯定太慢。在这种情况下,你可以做的最好的事情就是在矩阵元素(i
和j
上的循环)上并行化。
顺便说一句,行
i=2
j=2
l=41
代码中的是多余的,因为你立即将它们用作循环变量,以便它们将被覆盖。