11.2. MPI Hello World#
通信模式#
MPI 提供的能力更加底层,对于通信模式,通常有两种:双边和单边。
双边(Cooperative):通信双方均同意数据的收发。发送进程调用发送函数,接收进程调用接收函数。
单边(One-sided):一方远程读或者写另一方的数据,一方无需等待另一方。
World 和 Rank#
在进行 MPI 编程时,进程间通信需要首先解决两个基本问题:在MPI进程群中,如何识别“我是谁?”以及“除了我,还有哪些进程?”。MPI 中,MPI_Comm_rank
定义了每个进程的身份,即“我是谁?”;而 MPI_COMM_WORLD
定义了参与通信的所有进程集合,即“还有谁?”。编写 MPI 程序的第一步是创建一个通信域,称为“World”,它包含 size
个进程,每个进程都有一个唯一的识别码,即 Rank,其值取值范围为 0~ size-1
。
更严肃地表述:
在 MPI 中, World 是指所有参与并行计算的进程的集合。在一个 MPI 程序中,所有进程默认属于一个通信域,即
MPI_COMM_WORLD
。在这个通信域内,所有进程都能够相互通信。World 中的每个进程都有一个唯一的 Rank,Rank 用来标识该进程在通信域中的位置。由于每个进程有自己的 Rank 号码,程序员可以控制 Rank 为 0 的进程发送数据给 Rank 为 1 的进程。
案例:Hello World#
代码片段 11.1 使用一个简单的例子来演示 MPI 编程。
from mpi4py import MPI
comm = MPI.COMM_WORLD
print(f"Hello! I'm rank {comm.Get_rank()} of {comm.Get_size()} running on host {MPI.Get_processor_name()}.")
comm.Barrier()
在这段程序中,print()
在单个进程内执行的,用于打印出该进程的 Rank 和主机名。comm.Barrier()
确保所有进程同步,即它将每个进程暂时阻塞,直到所有进程都到达该同步点,之后才会继续执行 comm.Barrier()
之后的代码。在本例中,comm.Barrier()
之后没有其他操作,因此程序将在此之后退出。
如果在个人计算机上,启动 8 个进程,可以通过命令行执行以下命令:
!mpiexec -np 8 python hello.py
Hello! I'm rank 5 of 8 running on host lu-mbp.Hello! I'm rank 1 of 8 running on host lu-mbp.
Hello! I'm rank 2 of 8 running on host lu-mbp.
Hello! I'm rank 4 of 8 running on host lu-mbp.
Hello! I'm rank 6 of 8 running on host lu-mbp.
Hello! I'm rank 7 of 8 running on host lu-mbp.
Hello! I'm rank 3 of 8 running on host lu-mbp.
Hello! I'm rank 0 of 8 running on host lu-mbp.
不同厂商的 mpiexec
在参数上可能会有所区别。与 C/C++ 或 Fortran 不同,使用 mpi4py 的便利之处在于它不需要使用 mpicc
编译器进行编译,而是可以直接执行。
如果你有一个集群,该集群挂载了一个共享文件系统,即集群上每个节点上的挂载目录的内容是相同的,源代码 hello.py
和安装的 MPI mpiexec
也是完全一致的。可以通过以下命令启动多个 MPI 进程:
mpiexec –hosts h1:4,h2:4,h3:4,h4:4 –n 16 python hello.py
该启动命令将在 16 个进程上执行,16 个进程分布在 4 个计算节点上,每个节点运行了 4 个进程。如果节点数量较多,可以创建一个包含节点信息的文件,例如命名为 hf
,其内容如下:
h1:8
h2:8
然后执行以下命令:
mpiexec –hostfile hf –n 16 python hello.py
Communicator#
刚才我们提到了 World 的概念,并使用了 MPI_COMM_WORLD
,更准确地说,MPI_COMM_WORLD
是一个通信器(Communicator)。MPI 将进程划分到不同的组(Group)中,每个 Group 有不同的 Color,Group 和 Color 共同组成了 Communicator,或者说 Communicator 是 Group + Color,一个默认的 Communicator 就是 MPI_COMM_WORLD
。
对于一个进程,它可能属于多个 Communicator,因此在不同 Communicator 中的 Rank 也可能不同。图 11.2 (a)、(b)和(c)展示了三个不同的 Communicator,其中圆圈代表进程。当我们启动一个 MPI 程序时,就自动创建了默认的 Communicator:MPI_COMM_WORLD
,如 图 11.2 (a)所示。在每个 Communicator 内,每个进程都被分配了一个唯一的 Rank,图中圆圈上的数字表示进程在该 Communicator 中的 Rank。同一个进程可以属于不同的 Communicator,并且在不同 Communicator 中的 Rank 也可能不同,如 图 11.2 (b)和(c)所示。每个 Communicator 内的进程通信是相互独立的。对于大多数简单的程序而言,使用默认的MPI_COMM_WORLD已经足够。