11.2. MPI Hello World#

通信模式#

MPI 提供的能力更加底层,对于通信模式,通常有两种:双边和单边。

  • 双边(Cooperative):通信双方均同意数据的收发。发送进程调用发送函数,接收进程调用接收函数。

  • 单边(One-sided):一方远程读或者写另一方的数据,一方无需等待另一方。

../_images/communications.svg

图 11.1 两种通讯模式:双边和单边#

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 编程。

代码片段 11.1 hello.py#
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已经足够。

../_images/communicator.svg

图 11.2 Communicator#