Fortran 如何并行化嵌套循环

Fortran 如何并行化嵌套循环,fortran,mpi,openmp,openmpi,Fortran,Mpi,Openmp,Openmpi,下面是一个小的串行代码示例,它的结构与我的代码相同 PROGRAM MAIN IMPLICIT NONE INTEGER :: i, j DOUBLE PRECISION :: en,ei,es DOUBLE PRECISION :: ki(1000,2000), et(200),kn(2000) OPEN(UNIT=3, FILE='output.dat', STATUS='UNKNOWN') DO i = 1, 1000, 1 DO j = 1, 2000, 1

下面是一个小的串行代码示例,它的结构与我的代码相同

PROGRAM MAIN
IMPLICIT NONE
INTEGER          :: i, j
DOUBLE PRECISION :: en,ei,es
DOUBLE PRECISION :: ki(1000,2000), et(200),kn(2000)
OPEN(UNIT=3, FILE='output.dat', STATUS='UNKNOWN')
DO i = 1, 1000, 1
   DO j = 1, 2000, 1
      ki(i,j) = DBLE(i) + DBLE(j)
   END DO
END DO
DO i = 1, 200, 1
   en = 2.0d0/DBLE(200)*(i-1)-1.0d0
   et(i) = en
   es = 0.0d0
   DO j = 1, 1000, 1
      kn=ki(j,:)
      CALL CAL(en,kn,ei)
      es = es + ei
   END DO
   WRITE (UNIT=3, FMT=*) et(i), es
END DO
CLOSE(UNIT=3)
STOP
END PROGRAM MAIN

SUBROUTINE CAL (en,kn,ei)
IMPLICIT NONE
INTEGER          :: i
DOUBLE PRECISION :: en, ei, gf,p
DOUBLE PRECISION :: kn(2000)
p = 3.14d0
ei = 0.0d0
DO i = 1, 2000, 1
   gf = 1.0d0 / (en - kn(i) * p)
   ei = ei + gf
END DO
RETURN
END SUBROUTINE CAL
我在集群上运行我的代码,其中一个节点上有32个CPU,一个节点上有32个CPU共共享250 GB内存。我最多可以使用32个节点

每次完成内部循环时,都要收集一个数据。所有外部循环完成后,总共需要收集200个数据。如果只有一个CPU执行内部循环,则需要3天以上(超过72小时)

我想分别对内环和外环进行并行化?有人能建议如何并行化这个代码吗

我可以分别对内环和外环使用MPI技术吗?如果是,如何区分执行不同循环(内环和外环)的不同CPU

另一方面,我看到有人提到混合MPI和OpenMP方法的并行化。我可以对外循环使用MPI技术,对内循环使用OpenMP技术吗?如果是这样的话,如何在每次内循环完成后将一个数据收集到CPU,以及在所有外循环完成后将总共200个数据收集到CPU。如何区分分别执行内环和外环的不同CPU


或者,有人会提供关于并行化代码和提高效率的其他建议吗?提前非常感谢。

如评论中所述,一个好的答案需要更详细的问题。然而,乍一看,似乎并行化内部循环

DO j = 1, 1000, 1
  kn=ki(j,:)
  CALL CAL(en,kn,ei)
  es = es + ei
END DO
应该足以解决你的问题,或者至少它将是一个良好的开端。首先,我猜循环中有一个错误

DO i = 1, 1000, 1
  DO j = 1, 2000, 1
    ki(j,k) = DBLE(j) + DBLE(k)
  END DO
END Do
因为k被设置为0,并且没有地址对应于0的单元格(请参见变量声明)。另外,ki被声明为ki(10002000)数组,而ki(j,i)被声明为(20001000)数组。除了这些错误之外,我想ki应该计算为

ki(i,j) = DBLE(j) + DBLE(i)
如果是真的,我建议您使用以下解决方案

PROGRAM MAIN
IMPLICIT NONE
INTEGER          :: i, j, k,icr,icr0,icr1
DOUBLE PRECISION :: en,ei,es,timerRate
DOUBLE PRECISION :: ki(1000,2000), et(200),kn(2000)
INTEGER,PARAMETER:: nthreads=1
call system_clock(count_rate=icr)
timerRate=real(icr)
call system_clock(icr0)
call omp_set_num_threads(nthreads)
OPEN(UNIT=3, FILE='output.dat', STATUS='UNKNOWN')
DO i = 1, 1000, 1
  DO j = 1, 2000, 1
    ki(i,j) = DBLE(j) + DBLE(i)
  END DO
END DO

DO i = 1, 200, 1
  en = 2.0d0/DBLE(200)*(i-1)-1.0d0
  et(i) = en
  es = 0.0d0
  !$OMP PARALLEL DO private(j,kn,ei) firstpribate(en) shared(ki) reduction(+:es)
  DO j = 1, 1000, 1
    kn=ki(j,:)
    CALL CAL(en,kn,ei)
    es = es + ei
  END DO
  !$OMP END PARALLEL DO 
  WRITE (UNIT=3, FMT=*) et(i), es
END DO
CLOSE(UNIT=3)
call system_clock(icr1)
write (*,*) (icr1-icr0)/timerRate ! return computing time 
STOP

END PROGRAM MAIN

SUBROUTINE CAL (en,kn,ei)
IMPLICIT NONE
INTEGER          :: i
DOUBLE PRECISION :: en, ei, gf,p
DOUBLE PRECISION :: kn(2000)
p = 3.14d0
ei = 0.0d0

DO i = 1, 2000, 1
  gf = 1.0d0 / (en - kn(i) * p)
  ei = ei + gf
END DO

RETURN
END SUBROUTINE CAL
我添加了一些变量来检查计算时间;-)

对于n读取=1,此解在5.14 s内计算,对于n读取=2,此解在2.75 s内计算。它并没有将计算时间除以2,但对于第一次尝试来说,这似乎是一笔不错的交易。不幸的是,在这台机器上我有一个核心i3进程。所以我不能做得比nthreads=2更好。然而,我想知道,当nthreads=16时,代码将如何运行

请让我知道

我希望这对你有帮助


最后,我警告在实际代码中可以慎重考虑变量状态(私有、第一私有和共享)。

我担心回答这个问题确实需要更多细节。混合MPI+OpenMP可能是一种很好的方法,但要确定地说,您需要提供更多细节,特别是关于内存使用和数据依赖性的细节,请注意,MPI将要求您重写整个循环,甚至可能重写整个代码,因为每个处理器上需要不同的
j
起始值和结束值。你试过编译器开关吗?@Ian Bush,High Performance Mark and wander95非常感谢你的回答。我已经用一个小例子修改了我的帖子,序列代码和集群信息是我正在运行的代码。如果您能为并行化提供任何解决方案,我将不胜感激。或者,您是否可以使用混合MPI和OpenMP方法修改这个小型串行示例代码?再次非常感谢。谢谢你的例子。如果它被重新打开,我会设法找时间回答。但在考虑并行性之前,我应该指出的一点是,由于您以错误的顺序访问ki元素,串行性能将很差-您应该尝试编写代码,使您移动最快的索引位于第一个,而不是最后一个。因此,在并行性之前,我建议您重写代码以处理ki转置,而不是像上面所写的那样。我不会通过电子邮件这样做-我试图帮助您,但我不是一个代码编写服务。我正在准备一些教学,如果我能找到时间,一旦完成,我会看看。但是,我们的想法是在上面编写的小代码中,外部循环使用MPI,内部循环使用OpenMP。应该很容易,你为什么不试一试呢?非常感谢你的帮助。我已经在我的帖子中修改了关于变量ki的示例代码。你的回答对我很有帮助。我能再问一个问题吗?您使用OpenMP技术来并行化内部循环。仅将内部循环并行化就足够了。然而,在本例中,我正在编程的实际代码要复杂得多,我想并行化内环和外环。实际代码的结构与此示例代码相同。有可能同时并行内部和外部循环吗?再次非常感谢您的帮助。@Kieran由于OpenMP的概念,无法并行化两个循环。最好的方法总是并行化外部循环,但对于您提到的情况,我认为它足够有效地并行化内部循环。事实上,由于您对每个索引“i”在“es”上求和,而“es”不是向量,因此并行化内部循环似乎更方便,以便在不使用原子或关键同步选项的情况下获得reduce子句的好处。请注意,减少是最有效的。现在要并行化外部循环,你必须重新考虑变量的结构。@Kieran如果你并行化外部循环,你可能会按照不需要的顺序写“et(i),es”。非常感谢你的帮助。我现在正在尝试使用MPI来并行化外循环,使用OpenMP来并行化内循环。你关于“减价条款”的建议对我很有用。