Fortran 嵌套循环的并行程序的结果不同于串行程序

Fortran 嵌套循环的并行程序的结果不同于串行程序,fortran,openmp,gfortran,fortran95,Fortran,Openmp,Gfortran,Fortran95,我想将OpenMP用于此单线程代码: PROGRAM SINGLE INTEGER, DIMENSION(30000)::SUMGRM INTEGER, DIMENSION(90000)::GRI,H REAL*8::HSTEP1X,HSTEP2X REAL*8::TIME1,TIME2 !Just intiial value DO I=1, 30000 SUMGRM(I)=I*3 END DO DO I=1, 90000 GR

我想将OpenMP用于此单线程代码:

PROGRAM SINGLE
  INTEGER, DIMENSION(30000)::SUMGRM
  INTEGER, DIMENSION(90000)::GRI,H
  REAL*8::HSTEP1X,HSTEP2X
  REAL*8::TIME1,TIME2

!Just intiial value
  DO I=1, 30000
     SUMGRM(I)=I*3        
  END DO

  DO I=1, 90000
     GRI(I)=I
     H(I)=0.5*I/10000    
  END DO

!Computing computer's running time (start) : for serial programming
 CALL CPU_TIME(TIME1)

 DO K=1, 50000
    DO I=2, 30000
       HSTEP1X=0.0    
         DO J=SUMGRM(I-1)+1, SUMGRM(I)-1
            HSTEP2X=H(GRI(J))/0.99
            HSTEP1X=HSTEP1X+HSTEP2X       
         END DO
       HSTEP2X=H(GRI(SUMGRM(I)))/0.99
       HSTEP1X=HSTEP1X+HSTEP2X         
    END DO
 END DO

  PRINT *, 'Results  =', HSTEP1X
  PRINT *, '   '

!Computing computer's running time (finish) : for serial programming
 CALL CPU_TIME(TIME2)
 PRINT *, 'Elapsed real time = ', TIME2-TIME1, 'second(s)'
END PROGRAM SINGLE
如您所见,主要问题在于最内侧循环(
J
),这也是最外侧循环(
I
)的一个功能。我试着将这个程序并行化如下:

PROGRAM PARALLEL
  INTEGER, DIMENSION(30000)::SUMGRM
  INTEGER, DIMENSION(90000)::GRI,H
  REAL*8::HSTEP1X,HSTEP2X
  REAL*8::TIME1,TIME2,OMP_GET_WTIME
  INTEGER::Q2,P2

!Just intiial value
  DO I=1, 30000
     SUMGRM(I)=I*3        
  END DO

  DO I=1, 90000
     GRI(I)=I
     H(I)=0.5*I/10000  
  END DO

!Computing computer's running time (start) : for parallel programming
 TIME1= OMP_GET_WTIME()

 DO K=1, 50000
 !$OMP PARALLEL DO PRIVATE (HSTEP1X,Q2,P2)
    DO I=2, 30000
       HSTEP1X=0.0
       Q2=SUMGRM(I-1)+1
       P2=SUMGRM(I)-1
         DO J=Q2, P2
            HSTEP2X=H(GRI(J))/0.99
            HSTEP1X=HSTEP1X+HSTEP2X       
         END DO
       HSTEP2X=H(GRI(SUMGRM(I)))/0.99
       HSTEP1X=HSTEP1X+HSTEP2X     
    END DO
 !$OMP END PARALLEL DO
 END DO

 PRINT *, 'Results  =', HSTEP1X
 PRINT *, '   '

!Computing computer's running time (finish) : for parallel programming
 TIME2= OMP_GET_WTIME()
 PRINT *, 'Elapsed real time = ', TIME2-TIME1, 'second(s)'
END PROGRAM PARALLEL
我使用了
gfortran和-O3-fopenmp
,然后导出
OMP_NUM_THREADS=…
并行程序运行得更快,但结果与单线程代码不同。通过串行程序我得到了
12.1212
(这是正确的一个),通过并行程序我得到了
0.000
(一定是出了什么问题)


我做错了什么?

首先我们可以注意到,默认情况下,您可能会发现
j
hstep2x
都将在线程之间共享。我不认为这是您真正想要的,因为如果多个线程使用相同的迭代索引,但试图在不同的范围内循环,这将导致一些非常奇怪的行为

接下来让我们注意,串行代码实际上只是打印
i=30000
迭代的结果,因为
hstep1x
的值在每次迭代开始时重置为0。因此,为了在openmp代码中得到“正确”的答案,我们可以专注于复制最终的迭代——我认为这完全否定了在这里使用openmp的意义。我猜这只是一个简单的例子,你试图用它来表示你真正的问题——我想你可能在制作这个时忽略了一些真正的问题

然而,下面的代码在我的机器上生成“正确”答案。我不确定它有多灵活,但它在这里起作用

PROGRAM PARALLEL
  INTEGER, DIMENSION(30000)::SUMGRM
  INTEGER, DIMENSION(90000)::GRI,H
  REAL*8::HSTEP1X,HSTEP2X
  REAL*8::TIME1,TIME2,OMP_GET_WTIME
  INTEGER::Q2,P2

!Just intiial value                                                                                                                                                                                                  
  DO I=1, 30000
     SUMGRM(I)=I*3
  END DO

  DO I=1, 90000
     GRI(I)=I
     H(I)=0.5*I/10000
  END DO

!Computing computer's running time (start) : for parallel programming                                                                                                                                                
 TIME1= OMP_GET_WTIME()

 DO K=1, 50000
!$OMP PARALLEL DO PRIVATE (Q2,P2,J,HSTEP2X) DEFAULT(SHARED) LASTPRIVATE(HSTEP1X)                                                                                                                                     
    DO I=2, 30000
       HSTEP1X=0.0
       Q2= SUMGRM(I-1)+1
       P2= SUMGRM(I)-1
         DO J=Q2,P2
            HSTEP2X=H(GRI(J))/0.99
            HSTEP1X=HSTEP1X+HSTEP2X
         END DO
       HSTEP2X=H(GRI(SUMGRM(I)))/0.99
       HSTEP1X=HSTEP1X+HSTEP2X
    END DO
!$OMP END PARALLEL DO                                                                                                                                                                                                
END DO

 PRINT *, 'Results  =', HSTEP1X
 PRINT *, '   '

!Computing computer's running time (finish) : for parallel programming                                                                                                                                               
 TIME2= OMP_GET_WTIME()
 PRINT *, 'Elapsed real time = ', TIME2-TIME1, 'second(s)'
END PROGRAM PARALLEL
我在这里做了三件事:

  • 确保
    j
    hstep2x
    对每个线程都是私有的
  • 显式声明了要共享的默认行为(此处不需要,但无所谓)
  • 指定
    hstep1x
    lastprivate
    。这意味着退出并行区域后,
    hstep1x
    的值取自执行上一次迭代的线程。(详情请参见)

  • 首先,我们可以注意到,默认情况下,您可能会发现
    j
    hstep2x
    都将在线程之间共享。我不认为这是您真正想要的,因为如果多个线程使用相同的迭代索引,但试图在不同的范围内循环,这将导致一些非常奇怪的行为

    接下来让我们注意,串行代码实际上只是打印
    i=30000
    迭代的结果,因为
    hstep1x
    的值在每次迭代开始时重置为0。因此,为了在openmp代码中得到“正确”的答案,我们可以专注于复制最终的迭代——我认为这完全否定了在这里使用openmp的意义。我猜这只是一个简单的例子,你试图用它来表示你真正的问题——我想你可能在制作这个时忽略了一些真正的问题

    然而,下面的代码在我的机器上生成“正确”答案。我不确定它有多灵活,但它在这里起作用

    PROGRAM PARALLEL
      INTEGER, DIMENSION(30000)::SUMGRM
      INTEGER, DIMENSION(90000)::GRI,H
      REAL*8::HSTEP1X,HSTEP2X
      REAL*8::TIME1,TIME2,OMP_GET_WTIME
      INTEGER::Q2,P2
    
    !Just intiial value                                                                                                                                                                                                  
      DO I=1, 30000
         SUMGRM(I)=I*3
      END DO
    
      DO I=1, 90000
         GRI(I)=I
         H(I)=0.5*I/10000
      END DO
    
    !Computing computer's running time (start) : for parallel programming                                                                                                                                                
     TIME1= OMP_GET_WTIME()
    
     DO K=1, 50000
    !$OMP PARALLEL DO PRIVATE (Q2,P2,J,HSTEP2X) DEFAULT(SHARED) LASTPRIVATE(HSTEP1X)                                                                                                                                     
        DO I=2, 30000
           HSTEP1X=0.0
           Q2= SUMGRM(I-1)+1
           P2= SUMGRM(I)-1
             DO J=Q2,P2
                HSTEP2X=H(GRI(J))/0.99
                HSTEP1X=HSTEP1X+HSTEP2X
             END DO
           HSTEP2X=H(GRI(SUMGRM(I)))/0.99
           HSTEP1X=HSTEP1X+HSTEP2X
        END DO
    !$OMP END PARALLEL DO                                                                                                                                                                                                
    END DO
    
     PRINT *, 'Results  =', HSTEP1X
     PRINT *, '   '
    
    !Computing computer's running time (finish) : for parallel programming                                                                                                                                               
     TIME2= OMP_GET_WTIME()
     PRINT *, 'Elapsed real time = ', TIME2-TIME1, 'second(s)'
    END PROGRAM PARALLEL
    
    我在这里做了三件事:

  • 确保
    j
    hstep2x
    对每个线程都是私有的
  • 显式声明了要共享的默认行为(此处不需要,但无所谓)
  • 指定
    hstep1x
    lastprivate
    。这意味着退出并行区域后,
    hstep1x
    的值取自执行上一次迭代的线程。(详情请参见)
  • 你试过使用吗

    !$OMP PARALLEL DO DEFAULT(PRIVATE) REDUCTION(+:HSTEP1X)                                                                                                                                     
    
    你试过使用吗

    !$OMP PARALLEL DO DEFAULT(PRIVATE) REDUCTION(+:HSTEP1X)                                                                                                                                     
    

    所有fortran问题都使用标记ptag:fortran]。在需要区分的地方添加版本标记。注意订阅和订阅的人数,你想让更多的人看到你的问题,不是吗?可以说并行程序总是产生不同于串行程序的结果。具体说明结果的不同之处,从中可以学到很多东西。请尝试使用标题说明您的问题,而不仅仅是主题。标签很好地定义了主题。@VladimirF:你是什么意思?你的意思是我也想通过浏览量来获得人气吗?不,我从来都不想对我的问题有更多的看法,我真的不在乎。我只是想从一些专家那里得到最恰当的答案。我甚至不知道获得大量的观点有什么好处,除了我有更大的机会从一些专家那里得到答案。你能告诉我,在这个网站上是否禁止像我这样做?如果是的话,我很乐意将其删除。谢谢。@HighPerformanceMark:区别在于:通过单个程序,我得到了12.1212(这是真实的),通过并行程序,我得到了0.000(这一定是有问题的)。所有fortran问题都使用标记ptag:fortran]。在需要区分的地方添加版本标记。注意订阅和订阅的人数,你想让更多的人看到你的问题,不是吗?可以说并行程序总是产生不同于串行程序的结果。具体说明结果的不同之处,从中可以学到很多东西。请尝试使用标题说明您的问题,而不仅仅是主题。标签很好地定义了主题。@VladimirF:你是什么意思?你的意思是我也想通过浏览量来获得人气吗?不,我从来都不想对我的问题有更多的看法,我真的不在乎。我只是想从一些专家那里得到最恰当的答案。我甚至不知道获得大量的观点有什么好处,除了我有更大的机会从一些专家那里得到答案。你能告诉我,在这个网站上是否禁止像我这样做?如果