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
注意:这里后两个函数传入的参数是引用,即在进入并行后
size
和rank
存入的变量,实际起到的功能是保存进程参数
示例程序:
1 |
|
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_SOURCE
;tag
可以使用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