Fortran 嵌套循环的并行程序的结果不同于串行程序
我想将OpenMP用于此单线程代码: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
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:你是什么意思?你的意思是我也想通过浏览量来获得人气吗?不,我从来都不想对我的问题有更多的看法,我真的不在乎。我只是想从一些专家那里得到最恰当的答案。我甚至不知道获得大量的观点有什么好处,除了我有更大的机会从一些专家那里得到答案。你能告诉我,在这个网站上是否禁止像我这样做?如果