MPI 基础知识
MPI 基础知识
MPI (Message Passing Interface)
C/C++:
mpi,h
->libmpi.so
编译:mpicxx -o test_mpi_cpp test_mpi.cpp -lmpi
Fortran:
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)