MPI

MPI:Message Passing Interface,是一套消息传递库的标准,定义了接口,但并不定义具体实现

常用于分布式内存系统

设计目标:可移植性可扩展性灵活性

MPI采取SPMD编程风格,即单程序多数据,注意:

  • SPMD中程序的某个片段并行分支执行,使用不同区域的数据执行相同的程序段,但具体执行方式(例如程序段内有依赖数据的分支)可能不同
  • SIMD是指令级的单指令多数据,对不同的数据一定执行相同的操作
  • SPMD不是SIMD

MPI编程基础

下面介绍基于C++的MPI编程

首先,程序需要包含mpi.h头文件,然后对每个MPI调用采取rc = MPI_Xxx(xxx)的格式,如果正确执行,返回值rc = MPI_SUCCESS


在并行程序执行时的重要问题:

  • How many?——即总进程数,由MPI_Comm_size提供
  • Who am I?——即当前进程号,由MPI_Comm_rank提供

注意:MPI是分布式数据的进程级并行


MPI环境配置函数:

  • MPI_Init():初始化MPI运行环境
  • MPI_Finalize():终止MPI运行环境
  • MPI_Comm_size(comm, &size):确定通信域中的进程数量
  • MPI_Comm_rank(comm, &rank):确定通信域内的rank,即任务ID

注意:这里后两个函数传入的参数是引用,即在进入并行后sizerank存入的变量,实际起到的功能是保存进程参数


示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
#include "mpi.h"
#include <stdio.h>

int main (int argc, char *argv[]) {
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
printf("I am %d of %d!\n", rank, size);
MPI_Finalize();
return 0;
}

MPI通信

点对点通信分为两种方式:阻塞式非阻塞式

阻塞式

阻塞式通信中,函数在完成通信(即发送/接收完成后)才返回

MPI_Send(buffer, count, type, dest, tag, comm)

MPI_Recv(buffer, count, type, source, tag, comm, status)

这里的comm默认为MPI_COMM_WORLD

source可以使用MPI_ANY_SOURCEtag可以使用MPI_ANY_TAG

非阻塞式

非阻塞式通信中,函数调用后立即返回,并构造一个非阻塞通信对象request,以便后续查询通信状态

MPI_Isend(buffer, count, type, dest, tag, comm, request)

MPI_Irecv(buffer, count, type, source, tag, comm, request)

此时可以使用

MPI_Wait(request, status)

来等待之前发出的通信结束,或者使用

MPI_Waitall(count, array_of_requests, array_of_statuses)

来等待通信列表全部结束

消息传递模型的并行化策略

  • 将问题划分为子任务
  • 将子任务映射到不同的进程
  • 进程并行计算各自的子任务,之后将结果汇总

MPI集合通信

集合通信是MPI提供的一种比点对点通信更高效的通信手段


广播(一发多)

MPI_Bcast(&buffer, count, datatype, root, comm)

广播rank为root的进程的buffer,自动存入其他进程的buffer,例如

MPI_Bcase(&value, 1, MPI_INT, 0, MPI_COMM_WORLD)

会自动将所有进程的value值设为0号进程的对应值

归约(多发一)

MPI_Reduce(&sendbuf, &recvbuf, count, datatype, op, dest, comm)

将所有进程中的sendbuf中的值以某一操作op汇总后放入某一进程的recvbuf

例如dest = 2; count = 1; op = MPI_SUM则是将所有进程的sendbuf求和后放入2号进程的recvbuf

扫描

前缀版本的归约,操作数比归约少一个dest

例如,对以下情况使用MPI_SUM归约:

rank value
0 1
1 2
2 3
3 4

得到的结果为:

rank value
0 1
1 3
2 6
3 10

其他操作

这里略去具体使用方式和用途,可以随时查询

  • 分散(Scatter)
  • 收集(Gatter)
  • Allgather
  • Allreduce
  • Alltoall

屏障

MPI_Barrier(comm)

在组内创建barrier来同步,阻塞进程直到组内所有进程到达barrier处

通信域和组

组(Group):定义了一些可以相互通信的进程

通信域(Communicator):是一个与组关联的对象,用于执行通信函数调用

预先定义的全通信域为MPI_COMM_WORLD

可以如下定义新的通信域和组:

  • 使用MPI_Comm_group访问通信域关联的组

  • 使用MPI_Group_incl通过现有组的子集创建组

  • 使用MPI_Comm_create创建一个基于组的通信域

利用MPI_COMM_WORLD和上述接口就可以创建任意的通信域和组

MPI-IO

在MPI中使用常规的POSIX文件访问会面临性能不足的问题,因为不同进程访问同一文件会创建多个文件句柄

MPI提供了并行IO接口,使得不同进程访问同一文件时,文件在底层只打开一次,不同进程具有相同的文件句柄,可以同时读写

分为独立I/O和集合I/O两种

具体接口包括:

  • 打开文件:MPI_File_open
  • 关闭文件:MPI_File_close
  • 独立读写:MPI_File_read/write
  • 集合读写:MPI_File_read/write_all
  • 将写操作刷新到存储设备中:MPI_File_sync
  • MPI_File_seek
  • MPI_File_read_at

点对点通信的缓冲区问题