MPI库并行Fortran程序:进程通讯

用MPI库并行Fortran程序时,常常需要进程通讯以实现数组同步。这里的一个简单的例子可以说明MPI_SEND命令和MPI_RECV命令的用法,以实现点到点的通讯。

并行的目的

为什么要并行?

并行主要是为了将大型循环分摊到不同的CPU上,以节约时间。

最简单的例子

用三个进程分摊10次循环,为A(10)数组分别赋值1-10,最后三个进程相互通讯,把每个进程里的A(10)数列同步。

如何用多个CPU分摊循环?

三个CPU的进程编号分别为0,1,2。
进程0承担第3,6,9次循环。
进程1承担第1,4,7,10次循环。
进程2承担第2,5,8次循环。
最后三个进程相互通讯,把本进程里运算的结果也分享给其他CPU,即数列同步。

方法1:点对点通讯

MPI_SEND和MPI_RECV命令只针对两个进程之间。即一个进程接收,一个进程发送。
如果一个进程要发送给其他的2个进程的数据,需要发送2次,分别指定不同的终点和暗号。
如果一个进程要接收其他两个2个进程的数据,需要接收2次,分别指定不同的起点和暗号。

MPI_SEND命令

CALL MPI_SEND(buffer,count,datatype,destination,tag,comm,ierror)
该命令会将本进程里的buffer(count)数组或者变量发送到destination进程中,通讯“暗号”为tag,暗号一致即可通讯。详细选项如下:
buffer数组或整形,实数都可。为待发送的数组或者变量名。
count整形。为待发送数据的长度,单个变量的长度为1。
datatype为以下选择中的1个:

MPI DATATYPE Fortran DATATYPE
MPI_CHARACTER character(1)
MPI_INTEGER integer
MPI_REAL real
MPI_DOUBLE_PRECISION double precision,适合REAL(kind=8)
MPI_COMPLEX complex
MPI_LOGICAL logical
MPI_BYTE 8 binary digits

destination整形。为发送目的地的进程编号。
tag整形。为通讯标识,当接收和发送的通讯标识一致时才会进行收发,否则会等待下去。
comm通信器,一般使用系统预先定义的全局通信因子“MPI_COMM_WORLD”。
ierror输出。如果执行成功则会返回0。

MPI_RECV命令

CALL MPI_RECV(buffer,count,datatype,source,tag,comm,status,ierror)
该命令会将接收来自source进程里的buffer(count)数组或者变量,通讯“暗号”为tag,暗号一致即可通讯。详细选项如下:
buffer数组或整形,实数都可。为接收的数组或者变量名。
count整形。为接收数据的长度,单个变量的长度为1。
datatype同MPI_SEND命令。
source整形。为发送来源地的进程编号。
tag整形。为通讯标识,要与MPI_SEND命令中的通讯标识一致。
comm通信器,一般使用系统预先定义的全局通信因子“MPI_COMM_WORLD”。
status输出。0表示还未收到。
ierror输出。如果执行成功则会返回0。

示例程序

文件test_mpi.f程序如下

cloc 
      program test_mpi 
      USE MPI 
      INTEGER::  ICORE,NCORE,IERR,MASTER
      DIMENSION::A(10)
      DATA A/0,0,0,0,0,0,0,0,0,0/
C INITIALIZATION
      CALL MPI_INIT(IERR)
      CALL MPI_COMM_RANK(MPI_COMM_WORLD,ICORE,IERR)
      CALL MPI_COMM_SIZE(MPI_COMM_WORLD,NCORE,IERR)
      MASTER  = 0
      IF(ICORE.EQ.MASTER) WRITE(6,'(I3,"-CORES ASSIGNED")') NCORE
      CALL MPI_BARRIER(MPI_COMM_WORLD,IERR)
C INPUT DATA 单个进程为各自的部分赋值
      DO I    = 1,10
      ICE     = MOD(I,NCORE)
      IF(ICE.EQ.ICORE)  A(I)=1.0*I
      ENDDO
C COMMUNICATION 进程通讯,以同步变量
      DO 1  J = 1,10
      ICE     = MOD(J,NCORE)
      IF(ICE.EQ.ICORE)  THEN 
        TMP   = A(J)
        !在NCORE个进程中,只有第ICE个进程被赋值,它要将该值发送给剩下的NCORE-1个进程
        DO  I = 0,NCORE-1
        IF (I.NE.ICE) CALL MPI_SEND(TMP,1,MPI_REAL,I,
     &                I,MPI_COMM_WORLD,IERR)
        ENDDO
      ELSE 
        !每个进程分别接收来自ICE个进程发送来的数据,通讯标识号为自己的进程号
        CALL MPI_RECV(TMP,1,MPI_REAL,
     &       ICE,ICORE,MPI_COMM_WORLD,ISTATUS,IERR)
        A(J)  = TMP 
      ENDIF
1     CONTINUE
C OUTPUT DATA 输出进程0同步后的变量
      WRITE(6,'(I2,10F5.1)') ICORE,(A(I),I=1,10)
      CALL MPI_FINALIZE(IERR)
      end program

运行test_mpi.f程序

mpiifort -g test_mpi.f -o z #编译
mpirun -np 3 ./z #运行
mpirun -np 3 xterm -e gdb ./z #用GDB调试程序

运行结果,输出为:

  3-CORES ASSIGNED
 0  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0 10.0
 1  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0 10.0
 2  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0 10.0

方法2:通过广播同步变量

MPI_BCAST命令可以将一个进程里的某个变量或数组同步到所有进程中。

MPI_BCAST命令

CALL MPI_BCAST(buffer,count,datatype,root,comm,ierror)
该命令是将进程root中的buffer(count)数组或变量同步到其他所有进程中。注意只有出现该命令的进程才会接收广播。
buffer数组或整形,实数都可。为广播发出和收听的数组或者变量名。
count整形。为广播发出和收听数据的长度,单个变量的长度为1。
datatype同MPI_SEND或MPI_RECV命令。
root整形。广播发出的进程编号。如果root等于本进程号,则buffer既发出也接收;如果root不等于本进程编号,则本进程的buffer会接收广播发出的buffer。
comm通信器,一般使用系统预先定义的全局通信因子“MPI_COMM_WORLD”。
ierror输出。如果执行成功则会返回0。

广播示例

文件test_mpi.f如下

cloc 
      program test_mpi 
      USE MPI 
      INTEGER::  ICORE,NCORE,IERR,MASTER
      DIMENSION::A(10)
      DATA A/0,0,0,0,0,0,0,0,0,0/
C INITIALIZATION
      CALL MPI_INIT(IERR)
      CALL MPI_COMM_RANK(MPI_COMM_WORLD,ICORE,IERR)
      CALL MPI_COMM_SIZE(MPI_COMM_WORLD,NCORE,IERR)
      MASTER  = 0
      IF(ICORE.EQ.MASTER) WRITE(6,'(I3,"-CORES ASSIGNED")') NCORE
      CALL MPI_BARRIER(MPI_COMM_WORLD,IERR)
C INPUT AND BCAST DATA
      DO I    = 1,10
      ICE     = MOD(I,NCORE)
      !单个进程为数组A(10)中各自的部分赋值
      IF(ICE.EQ.ICORE)  THEN 
      A(I)    = 0.1+I*1.0
      ENDIF
      !赋值之后马上广播出去,将数组A(10)从ICE进程广播到其他进程中,以实现各个进程中的A数组同步
      CALL MPI_BCAST(A,10,MPI_REAL,ICE,MPI_COMM_WORLD,IERR)
      ENDDO
C OUTPUT DATA
      WRITE(6,'(I2,10F5.1)') ICORE,(A(I),I=1,10)
      CALL MPI_FINALIZE(IERR)
      end program

运行test_mpi.f程序

mpiifort -g test_mpi.f -o z #编译
mpirun -np 3 ./z #运行
mpirun -np 3 xterm -e gdb ./z #用GDB调试程序

运行结果,输出为:

  3-CORES ASSIGNED
 0  1.1  2.1  3.1  4.1  5.1  6.1  7.1  8.1  9.1 10.1
 1  1.1  2.1  3.1  4.1  5.1  6.1  7.1  8.1  9.1 10.1
 2  1.1  2.1  3.1  4.1  5.1  6.1  7.1  8.1  9.1 10.1
posted @ 2023-02-09 16:08  Philbert  阅读(620)  评论(0编辑  收藏  举报