0

关于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 符合预期。

但是,当我只调整 suml和的顺序时{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

我应该修改这段代码的哪一部分?

非常感谢您的帮助。

4

2 回答 2

2

你有一个竞争条件。不同的线程l试图使用相同的元素M(i,j)。您可以使用 Intel Inspector 或 Oracle Thread Analyzer 之类的工具来找到它(我与 Intel 进行了检查)。最好的办法是使用您的原始订单。您也可以使用归约,但要小心较大的数组:

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
于 2013-06-09T07:46:46.670 回答
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

在您的代码中是多余的,因为您立即将它们用作循环变量,以便它们无论如何都会被覆盖。

于 2013-06-09T06:42:38.597 回答