涉及三个do循环时的openmp问题(fortran)

时间:2013-06-09 03:07:45

标签: fortran openmp

我对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

我应该在哪个部分修改此代码?

非常感谢您的帮助。

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

每个线程都有一个自己的嵌套循环结构ij覆盖所有矩阵元素。因此,不同的线程将同时访问矩阵的相同元素。结果再次是不确定的。当然,您可以尝试通过确保通过OpenMP构造解决问题,线程在访问某个矩阵元素之前相互等待。但是,这会使算法肯定太慢。在这种情况下,你可以做的最好的事情就是在矩阵元素(ij上的循环)上并行化。

顺便说一句,行

i=2
j=2
l=41
代码中的

是多余的,因为你立即将它们用作循环变量,以便它们将被覆盖。