Parallel processing 为什么在启动更多进程时,基于Fortran的MPI例程需要更多的计算时间
我基于Fortran的MPI代码不涉及进程之间的通信。只对每个进程进行计算并计时。我的平台是英特尔桑迪桥。代码是使用MPiFort包装器编译的。我有两个无法解释的观察结果: 1) 计算时间随着涉及更多机器而增加,其中每台机器有16个核(2个处理器,每个处理器8个核)。例如,16个MPI级别最多耗时5.74秒,32个级别最多耗时13.64秒,48个级别最多耗时18.26秒,而64个级别最多耗时25.92秒。由于不涉及任何通信,所以我希望无论如何启动名称MPI列组,都能获得相同的时间。代码包括在下面 2) 在下面代码的步骤2中,调用了一个子例程。如果我用实际代码替换子程序调用,程序运行会更快。例如,16个MPI等级最多耗时5.63E-02秒,32个等级最多耗时0.1762秒,48个等级最多耗时0.33秒,而64个等级最多耗时0.3612秒 使用ifort编译的程序的串行版本也表现出类似的行为:使用子例程调用需要0.77秒,不使用子例程则需要5.19E-02秒 我在这里附上代码。第一个是无子例程调用,第二个是有子例程调用,第三个是子例程本身 mult.f(无子例程调用): mult.f(带子程序) mysubroutine(子例程调用): 使用以下命令编译代码:Parallel processing 为什么在启动更多进程时,基于Fortran的MPI例程需要更多的计算时间,parallel-processing,fortran,mpi,Parallel Processing,Fortran,Mpi,我基于Fortran的MPI代码不涉及进程之间的通信。只对每个进程进行计算并计时。我的平台是英特尔桑迪桥。代码是使用MPiFort包装器编译的。我有两个无法解释的观察结果: 1) 计算时间随着涉及更多机器而增加,其中每台机器有16个核(2个处理器,每个处理器8个核)。例如,16个MPI级别最多耗时5.74秒,32个级别最多耗时13.64秒,48个级别最多耗时18.26秒,而64个级别最多耗时25.92秒。由于不涉及任何通信,所以我希望无论如何启动名称MPI列组,都能获得相同的时间。代码包括在下面
mpiifort -O3 -mcmodel=large -xavx mult.f rose.f -o baseline
这里有两个截然不同的问题:
m
循环中。但是如果你看一下你的实际计算,你会发现,尽管你确实使用了dudr
数组来更新它,但是你总是从dudr(t8,t4,t6,ie)=0开始。所以它以前的值是不相关的,因为您删除了它。因此,只有m
循环的最后一次迭代才有任何效果
但是为了让编译器看到这一点,它需要看到例程的主体。因此,通过调用一个子例程,您可以强制编译器实际执行5001次,而通过内联它,您可以让它有机会意识到它是毫无意义的,并且只有一次迭代才能做到这一点
嗯,这主要是我的猜测,但我已经看到了很多次,我相信我离真相不远。事实上,由于您随后没有对dudr
执行任何操作,我甚至对编译器从您的大循环中生成任何内容感到惊讶
更多MPI进程需要更长的时间
对于这一个,我将做一个更疯狂的猜测:我相信在某种程度上,不是在几个节点中提交,而是在一个节点上错误地提交了代码。在那里,许多MPI进程竞相访问资源(尤其是内存带宽),并且随着进程数量的增加,时间越来越长。。。我可能错了,但目前,这是我得到的最好的解释
智慧之词
当您尝试对性能进行基准测试时,请确保始终向编译器隐藏您对计算的实际结果不感兴趣的信息。这可以通过打印一些值或将其传递给外部例程来实现(可能什么都不做,但编译器不会知道)。否则,您可能会惊讶于它有多么聪明,它能删除多少死代码。您在该程序中从何处发送或接收任何消息?并行性在哪里?如果你把它全部放在程序中,那么优化者可能会发现,由于dudr在循环之后从未被引用,它可以完全优化循环结构,如果调用一个子例程,这将更加困难。如果在程序结束时将dudr写入文件,会发生什么情况?哦,请把你的fortran从80年代拖出来。至少请使用隐式无。MPI问题可能是由于1个节点上的内存带宽问题,以及多个节点上的错误进程放置。您确定所有进程都不在1个节点上吗?再次感谢您富有洞察力的回答。我想你把他们俩都钉死了。我将做一些进一步的实验,并发布研究结果。我在这个集群上使用的是mpirun而不是srun。因此,对于srun,我不再看到更多MPI进程需要更长时间的问题。非常感谢。此外,我还在代码中添加了外部例程。时间没有改变,但你的建议被采纳了。
program mul
include 'mpif.h'
integer DIM, dim1, dim2, dim3, E
parameter (DIM=8, E=512, dim1=DIM, dim2=DIM, dim3=DIM)
integer ierr, rank, size
real*8 A(dim1, dim1), B(dim2, dim2), C(dim3, dim3)
real*8 u(dim1, dim2, dim3, E)
real*8 dudr (dim1, dim2, dim3, E), duds (dim1, dim2, dim3, E)
real*8 dudt (dim1, dim2, dim3, E), dudr2 (dim1, dim2, dim3)
integer val, i, j, k, l, r, s, t, start, end
integer iseed /3/
real tbeg, tend
CHARACTER(len=32) :: arg
call getarg(1, arg)
call MPI_INIT (ierr)
call MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
! Step 1: Initialize
do i = 1, dim1
do j = 1, dim1
A (i, j) = ran(iseed);
enddo
enddo
do i = 1, dim2
do j = 1, dim2
B (i, j) = A(j, i);
enddo
enddo
do i = 1, dim3
do j = 1, dim3
C (i, j) = A(j, i);
enddo
enddo
do m = 1, E
do i=1, dim1
do j=1, dim2
do k = 1, dim3
u (i, j, k, m) = (ran(iseed)*400.0) - 200.0
enddo
enddo
enddo
enddo
! Step 2: Compute derivatives
tbeg = mpi_wtime()
do m = 0, 5000
do ie=1, E
call mysubroutine(u, C, dudr, ie)
enddo
enddo
tend = mpi_wtime()
print *, DIM, E, tend-tbeg, rank
call MPI_FINALIZE (ierr)
end
SUBROUTINE mysubroutine(u, a, dudr, ie)
integer DIM, dim1, dim2, dim3, E
parameter (DIM=8, E=512, dim1=DIM, dim2=DIM, dim3=DIM)
real*8 u(DIM, DIM, DIM, E)
real*8 a(DIM, DIM)
real*8 dudr (DIM, DIM, DIM, E)
integer t8
integer t6
integer t4
integer t2
integer i
integer j
integer k
integer l
!dir$ ASSUME_ALIGNED a: 64
!dir$ ASSUME_ALIGNED u: 64
!dir$ ASSUME_ALIGNED dudr: 64
DO t2=1,8
DO t4=1,8
DO t6=1,8
DO t8=1,8
dudr(t8,t4,t6,ie)
$ =0
dudr(t8,t4,t6,ie)=dudr(t8,t4,t6,ie)+u(t2,t4,t6,ie)
$ *a(t8,t2)
ENDDO
DO t8=1,8
dudr(t8,t4,t6,ie)=dudr(t8,t4,t6,ie)
$ +u(t2,t4,t6,ie)*a(t8,t2)
ENDDO
ENDDO
ENDDO
ENDDO
END
mpiifort -O3 -mcmodel=large -xavx mult.f rose.f -o baseline