MPI 基础知识
MPI 基础知识
MPI (Message Passing Interface)
C/C++:
mpi,h->libmpi.so编译:mpicxx -o test_mpi_cpp test_mpi.cpp -lmpiFortran:
mpif.h, 或者使用模板mpiuse mpi->libmpi_mpifh.so编译:``mpifort test_mpi.F90 -o test_mpi_fortran`运行
mpirun -n 8 ./tests/mpi_tests/test_mpi_cpp
核心概念
消息:数据(数,数组,字符等等)
通信域(communicating domain):定义一组能够互相发消息的进程,每个进程分配一个序号(rank),通信可以通过rank或者消息标签tag来进行
MPI_COMM_WORLD: 所有进程的集合,最大通信域;进入并行环境时自动创建。
MPI_COMM_SELF:只包含使用它的进程
函数格式:
C/C++:
int MPI_Comm_size(MPI_Comm comm, int *size);Fortran:
call MPI_Comm_size(comm, size, ieror)C++:
int MPI::COMM::Get_size() const;
6个基本函数
MPI_Init(ierr): 初始化并行环境,通信域MPI_COMM_WORLD形成
MPI_Finalize(ierr): 退出并行环境
MPI_Comm_size(comm, size, ierr): 返回进程数size
MPI_Comm_rank(comm, rank, iree): 返回进程编号rank, 0~size-1
MPI_Send(buf, count, datatype, dest, tag, comm, ierr): 消息发送
MPI_Recv(buf, count, datatype, source, tag, comm, status, ierr): 消息接收
消息缓冲(MessageBuffer):消息起始地址(
buf),数据个数(count), 消息类型(datatype) 实际长度= datatype * count消息信封(Message Envelop):接收/发送进程号(
dest/source),消息标签 0-32767(tag),通信域(comm)
status:消息接收的状态
其他函数
MPI_Abort(comm, errorcode, ireror): 出现特殊情况,需要中途停止MPI
MPI_Get_count(status, datatype, count, ierror): 接收数据的数量
MPI_Pack_size(incount, datatype, comm, size, ierror): 确定要打包的数据占用空间大小
MPI_Pack(inbuf, incount, datatype, outbuf, outsize, position, comm, ierror):将数据打包,由<inbuf, incount, datatype>指定的消息到<outbuf, outsize>指定的空间
点对点通信 (P2P Communication)
通信模式
同步发送(Synchrounous Send)
缓冲发送(Buffered Send)
标准发送(Standed Send)
就绪发送(Ready Send)
发送类别:4种通信模式 * (阻塞+ 非阻塞)
MPI_Send(buf, count, datatype, dest, tag, comm, ierr)
MPI_Isend(buf, count, datatype, dest, tag, comm, request, ierror): 标准非阻塞通信 request:通信请求
MPI_Ssend(buf, count, datatype, dest, tag, comm, ierror): 标准同步发送
MPI_Issend(buf, count, datatype, dest, tag, comm, request, ierror)
MPI_Bsend(buf, count, datatype, dest, tag, comm, ierror):具有用户指定缓冲的基本发送
MPI_Ibsend(buf, count, datatype, dest, tag, comm, request, ierror)
MPI_Rsend(buf, count, datatype, dest, tag, comm, ierror)
MPI_Irsend(buf, count, datatype, dest, tag, comm, request, ierror)接收类别:阻塞和非阻塞
MPI_Recv(buf, count, datatype, source, tag, comm, status, ierror)
MPI_Irecv(buf, count, datatype, source, tag, comm, request, ierror)
群集通信(Collective Communication)
| 类型 | 函数名 | 含义 |
|---|---|---|
| 通信 | MPI_Bcast | 一对多广播同样的消息 |
| MPI_Gather | 多对一收集各进程信息 | |
MPI_Gatherv | 一般化 | |
| MPI_Allgather | 全局收集,每个进程执行 Gather | |
MPI_Allgatherv | 一般化 | |
| MPI_Scatter | 一对多散播不同消息 | |
MPI_Scatterv | 一般化 | |
| MPI_Alltoall | 多对多全局交换,每个进程执行 Scatter | |
MPI_Alltoallv | 一般化 | |
| 聚集 | MPI_Reduce | 多对一归约 |
MPI_Allreduce | 一般化 | |
MPI_Reduce_scatter | 归约并散播 | |
| MPI_Scan | 扫描。每个进程对自己前面的进程归约 | |
| 同步 | MPI_Barrier | 路障同步 |
多对一和一对多还需要指明root进程,多对多只需要指明通信域。
广播(MPI_Bcast)
MPI_Bcast(buffer, count, datatype, root, comm, ierror)
一对多,消息相同。有一个root进程,由它向所有进程发送消息。 对于root本身,缓冲区即是发送缓冲又是接收缓冲。
sequenceDiagram
participant P0 as P0 (Root)
participant P1 as P1
participant P2 as P2
participant P3 as P3
P0->>P1: MPI_Bcast(A)
P0->>P2: MPI_Bcast(A)
P0->>P3: MPI_Bcast(A)
Note right of P0: 所有进程收到相同数据sequenceDiagram
participant P0 as P0 (Root)
participant P1 as P1
participant P2 as P2
participant P3 as P3
P0->>P1: MPI_Bcast(A)
P0->>P2: MPI_Bcast(A)
P0->>P3: MPI_Bcast(A)
Note right of P0: 所有进程收到相同数据收集(MPI_Gather)
MPI_Gather(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, comm, ierror)
多对一,消息相同。有一个root进程用来接收,其他(包含root)发送。 这n个消息按进程的标号进行拼接。所有非root进程忽略接收缓冲。
sequenceDiagram
participant P0 as P0 (Root)
participant P1 as P1
participant P2 as P2
participant P3 as P3
Note over P1,P3: 各进程持有不同数据
P1->>P0: MPI_Gather(A)
P2->>P0: MPI_Gather(B)
P3->>P0: MPI_Gather(C)
Note over P0: 收集结果: [A,B,C]sequenceDiagram
participant P0 as P0 (Root)
participant P1 as P1
participant P2 as P2
participant P3 as P3
Note over P1,P3: 各进程持有不同数据
P1->>P0: MPI_Gather(A)
P2->>P0: MPI_Gather(B)
P3->>P0: MPI_Gather(C)
Note over P0: 收集结果: [A,B,C]散播(MPI_Scatter)
MPI_Scatter(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, comm, ierror)
一对多,消息不同,顺序存储。有一个root进程,将发送缓冲中的数据按进程编号,有序的发送。非root进程忽略发送缓冲
sequenceDiagram
participant P0 as P0 (Root)
participant P1 as P1
participant P2 as P2
participant P3 as P3
Note over P0: 初始数据: [A,B,C,D]
P0->>P1: MPI_Scatter(A)
P0->>P2: MPI_Scatter(B)
P0->>P3: MPI_Scatter(C)
Note left of P0: 最后一个元素D通常留在P0
Note right of P1: 各进程获得不同数据sequenceDiagram
participant P0 as P0 (Root)
participant P1 as P1
participant P2 as P2
participant P3 as P3
Note over P0: 初始数据: [A,B,C,D]
P0->>P1: MPI_Scatter(A)
P0->>P2: MPI_Scatter(B)
P0->>P3: MPI_Scatter(C)
Note left of P0: 最后一个元素D通常留在P0
Note right of P1: 各进程获得不同数据全局收集(MPI_Allgather)
MPI_Allgather(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, ierror)
相当于每个进程都作为root进程执行了一次Gather操作。
flowchart LR
subgraph 初始数据
P0((P0:A))
P1((P1:B))
P2((P2:C))
P3((P3:D))
end
subgraph Allgather结果
P0r[P0:A,B,C,D]
P1r[P1:A,B,C,D]
P2r[P2:A,B,C,D]
P3r[P3:A,B,C,D]
end
初始数据 -->|MPI_Allgather| Allgather结果flowchart LR
subgraph 初始数据
P0((P0:A))
P1((P1:B))
P2((P2:C))
P3((P3:D))
end
subgraph Allgather结果
P0r[P0:A,B,C,D]
P1r[P1:A,B,C,D]
P2r[P2:A,B,C,D]
P3r[P3:A,B,C,D]
end
初始数据 -->|MPI_Allgather| Allgather结果全局交换(MPI_Alltoall)
MPI_Alltoall(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm, ierror)
相当于每个进程作为root进程执行了一次Scatter操作。
flowchart LR
%% 初始数据
subgraph 初始数据
P0["P0: [A0,A1,A2,A3]"]
P1["P1: [B0,B1,B2,B3]"]
P2["P2: [C0,C1,C2,C3]"]
P3["P3: [D0,D1,D2,D3]"]
end
%% 通信规则(简化为关键路径)
P0 -->|A1| P1
P0 -->|A2| P2
P0 -->|A3| P3
P1 -->|B0| P0
P2 -->|C0| P0
P3 -->|D0| P0
%% 最终结果(突出显示)
subgraph 交换结果
P0_Result["P0: [A0,B0,C0,D0]"]
P1_Result["P1: [A1,B1,C1,D1]"]
P2_Result["P2: [A2,B2,C2,D2]"]
P3_Result["P3: [A3,B3,C3,D3]"]
end
%% 连接线
初始数据 --> 交换结果
style P0_Result fill:#e3f2fd,stroke:#2196f3
style P1_Result fill:#e8f5e9,stroke:#4caf50flowchart LR
%% 初始数据
subgraph 初始数据
P0["P0: [A0,A1,A2,A3]"]
P1["P1: [B0,B1,B2,B3]"]
P2["P2: [C0,C1,C2,C3]"]
P3["P3: [D0,D1,D2,D3]"]
end
%% 通信规则(简化为关键路径)
P0 -->|A1| P1
P0 -->|A2| P2
P0 -->|A3| P3
P1 -->|B0| P0
P2 -->|C0| P0
P3 -->|D0| P0
%% 最终结果(突出显示)
subgraph 交换结果
P0_Result["P0: [A0,B0,C0,D0]"]
P1_Result["P1: [A1,B1,C1,D1]"]
P2_Result["P2: [A2,B2,C2,D2]"]
P3_Result["P3: [A3,B3,C3,D3]"]
end
%% 连接线
初始数据 --> 交换结果
style P0_Result fill:#e3f2fd,stroke:#2196f3
style P1_Result fill:#e8f5e9,stroke:#4caf50归约(MPI_Reduce)
MPI_Reduce(sendbuf, recvbuf, count, datatype, op, root, comm, ierror)
多对一,且完成计算。每个进程将发送缓冲区中数据发到root进 程,root完成指定的操作,并将结果放到接收缓冲。
flowchart TD
%% 初始数据
subgraph 初始数据
P0["P0: A"]
P1["P1: B"]
P2["P2: C"]
P3["P3: D"]
end
%% 归约操作(以求和为例)
subgraph 归约过程
P0 -->|A| Root
P1 -->|B| Root
P2 -->|C| Root
P3 -->|D| Root
Root["MPI_Reduce(OP=SUM)"] --> 结果
end
%% 最终结果
结果["Root进程结果: A+B+C+D"]
%% 样式
style Root fill:#ffebee,stroke:#f44336
style 结果 fill:#e8f5e9,stroke:#4caf50flowchart TD
%% 初始数据
subgraph 初始数据
P0["P0: A"]
P1["P1: B"]
P2["P2: C"]
P3["P3: D"]
end
%% 归约操作(以求和为例)
subgraph 归约过程
P0 -->|A| Root
P1 -->|B| Root
P2 -->|C| Root
P3 -->|D| Root
Root["MPI_Reduce(OP=SUM)"] --> 结果
end
%% 最终结果
结果["Root进程结果: A+B+C+D"]
%% 样式
style Root fill:#ffebee,stroke:#f44336
style 结果 fill:#e8f5e9,stroke:#4caf50扫描(MPI_Scan)
MPI_Scan(*sendbuf*, *recvbuf*, *count*, *datatype*, *op*, *comm*, *ierror*)
多对多。一种特殊的Reduce:每一个进程多作为root对排在他前面的进程执行归约操作。(类比前缀和)
flowchart LR
%% 初始数据
subgraph 初始数据
P0["P0: A"]
P1["P1: B"]
P2["P2: C"]
P3["P3: D"]
end
%% 扫描过程(以累加为例)
subgraph 前缀扫描
direction TB
P0 -->|A| Scan0["P0结果: A"]
P0 -->|A| P1
P1 -->|A+B| Scan1["P1结果: A+B"]
P1 -->|A+B| P2
P2 -->|A+B+C| Scan2["P2结果: A+B+C"]
P2 -->|A+B+C| P3
P3 -->|A+B+C+D| Scan3["P3结果: A+B+C+D"]
end
%% 样式
style Scan0 fill:#e1f5fe,stroke:#039be5
style Scan3 fill:#e8f5e9,stroke:#4caf50flowchart LR
%% 初始数据
subgraph 初始数据
P0["P0: A"]
P1["P1: B"]
P2["P2: C"]
P3["P3: D"]
end
%% 扫描过程(以累加为例)
subgraph 前缀扫描
direction TB
P0 -->|A| Scan0["P0结果: A"]
P0 -->|A| P1
P1 -->|A+B| Scan1["P1结果: A+B"]
P1 -->|A+B| P2
P2 -->|A+B+C| Scan2["P2结果: A+B+C"]
P2 -->|A+B+C| P3
P3 -->|A+B+C+D| Scan3["P3结果: A+B+C+D"]
end
%% 样式
style Scan0 fill:#e1f5fe,stroke:#039be5
style Scan3 fill:#e8f5e9,stroke:#4caf50屏障(MPI_Barrier)
MPI_Barrier(comm, ierror)