MPI 学习笔记(一)
程序员文章站
2022-03-15 19:24:33
...
MPI学习笔记(一)
文章目录
MPI_Init —— 初始化
int MPI_Init(int argc, char**argv){}
/*
MPI_Init是MPI程序第一个调用,完成MPI程序的所有初始化操作。所有MPI程序第一条可执行语句都是这一句。
要求main必须带参数运行,否则会出错
*/
MPI_Finalize —— 结束
int MPI_Finalize(void){}
/*
MPI程序最后一个调用,是MPI的最后一条可执行语句,否则程序运行将不可预测。
标志着并行代码结束,结束除了主进程外其他进程
*/
MPI_Comm_size —— 获取进程个数
int MPI_Comm_size(MPI_Comm comm, int *size){}
MPI_Comm_rank —— 获取进程rank(id)
int MPI_Comm_rank(MPI_Comm comm, int *rank){}
//返回一个rank值,rank的范围在0-p-1之间,相当于进程的id。
以上方法例子
#include <stdio.h>
#include "mpi.h"
int main(int argc, char *argv[])
{
int myid, numprocs;
int namelen;
char processor_name[MPI_MAX_PROCESSOR_NAME];
/* INIT */
MPI_Init(&argc, &argv);
/* GET ID */
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
/* GET SIZE */
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
/* GET NAME */
MPI_Get_processor_name(process_name, &namelen);
/* OUTPUT */
fprintf(stderr, "Hello! Process %d of %d on %s\n", myid, numprocs, processor_name);
/* END */
MPI_Finalize();
}
/* OUTPUT RESULT */
/* 输出和执行顺序有关 */
/*
Hello! Process 4 of 8 on ubuntu
Hello! Process 2 of 8 on ubuntu
Hello! Process 6 of 8 on ubuntu
Hello! Process 1 of 8 on ubuntu
Hello! Process 5 of 8 on ubuntu
Hello! Process 7 of 8 on ubuntu
Hello! Process 0 of 8 on ubuntu
Hello! Process 3 of 8 on ubuntu
*/
MPI数据传送
- 数据类型相匹配
- Send和Recv相匹配,避免死锁
- 消息标签匹配
/* filename: greet.c */
#include <stdio.h>
#include "mpi.h"
int main(int argc, char *argv[]){
int myid, numprocs, source;
MPI_Status status;
char message[100];
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
if(myid != 0){
sprintf(message, "Hello! From process %d", myid);
/* SEND MESSAGE */
MPI_Send(message, strlen(message)+1, MPI_CHAR, 0, 99, MPI_COMM_WORLD);
} else {
for(source = 1; source < numprocs; source++){
/* RECEICE MESSAGE */
MPI_Recv(message, 100, MPI_CHAR, source, 99, MPI_COMM_WORLD, &status);
printf("%s\n", message);
}
}
MPI_Finalize();
}
/* OUTPUT */
/*
Hello! From process 1
Hello! From process 2
Hello! From process 3
Hello! From process 4
Hello! From process 5
Hello! From process 6
Hello! From process 7
*/
MPI 数据类型
- MPI 中所有数据类型均为 MPI 自定义类型.
- 基本数据类型: MPI_INT, MPI_CHAR…
- 用户自定义数据类型或派生数据类型
- 目的
- 异构计算
不同系统有不同的数据表示格式. MPI 预先定义一些基本数据类型, 在实现过程中这些基本数据类型为桥梁进行转换. - 派生数据类型
允许消息来自不连续的和类型不一致的存储区域, 如数组散元与结构类型的传送.
- 异构计算
MPI | C |
---|---|
MPI_BYTE | |
MPI_CHAR | char |
MPI_DOUBLE | double |
MPI_FLOAT | float |
MPI_INT | int |
MPI_LONG | long |
MPI_SHORT | short |
MPI_UNSIGNED | unsigned |
MPI_UNSIGNED_CHAR | unsigned char |
MPI_UNSIGNED_LONG | unsigned long |
MPI_UNSIGNED_SHORT | unsigned short |
MPI_PACKED | / |
数据类型匹配规则
- 有类型的数据的通信.发送接收数据类型必须相同
- 无类型数据通信, 以
MPI_BYTE
为数据类型 - 打包数据的通信, 以
MPI_PACKED
为数据类型
通信组/通信子:MPI_COMM_WORLD
- 一个通信组是一个进程组的集合。所有参与并行计算的进程可以组合为一个或多个通信组
- 执行
MPI_Init
后,一个MPI程序的所有进程形成一个缺省的组,这个组被写作MPI_COMM_WORLD
- 该参数是MPI通信操作函数中必不可少的参数,用于限定参加通信的进程的范围
数据的发送和接收
MPI_Send 阻塞发送(Blocking Send)
int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm){}
/*
* buf: 接收缓存区的首地址
* count: 需要发送的字节数
* datatype: 每个发送元素的数据类型
* dest: 目标的rank(id)
* tag: 消息标识
* comm: 通信域
*
**/
MPI_Recv 阻塞接收(Blocking Receive)
int MPI_Recv(const void *buf, int count, MPI_Datatype datatype, int src, int tag, MPI_Comm comm, MPI_Status *status){}
/*
* buf: 接收缓存区的首地址
* count: 需要发送的字节数
* datatype: 每个发送元素的数据类型
* src: 来源的rank(id)
* tag: 消息标识
* comm: 通信域
* status: status对象,包含实际接收到的消息有关信息
*
**/
消息信封(MEssage Envelop)
MPI标识一条消息的信息包含四个域:
- Source: 发送进程隐式确定, 由进程的rank值唯一标识.
- Destination: Send函数参数确定
- Tag: Send函数参数确定
- Communicator: 默认
MPI_COMM_WORLD
- Group: 有限, 有序
- Contex: Super_tag, 用于标识该通讯空间
消息标签(Tag)
使用消息标签的意义在于,避免两个进程中传递多个消息时,进程发送消息先后到达出现的冲突.可将本次发送的消息与同一进程向同一目的进程发送的其他消息区分开来.
消息匹配
- 接收 buffer 必须至少容纳 count 个 datatype , 如果接收的 buffer 太小,将会溢出.
- 消息匹配
- 参数匹配(dest, tag, comm/source, tag, comm)
- Source ==
MPI_ANY_SOURCE
; 接收任意处理器来的数据 - Tag ==
MPI_ANT_TAG
匹配任意 tag 值得消息.
- 在阻塞式消息传送中不允许
Source == Dest
, 否则会导致死锁. - 消息传送被限制在同一个
communicator
. - 在 Send 函数中应该指定唯一的接收进程.
status 参数
-
使用
MPI_ANY_SOURCE
和MPI_ANY_TAG
接收消息时, 可通过status
的属性来获取source
和tag
的来源.status.MPI_SOURCE; status.MPI_TAG;
-
获取实际接收到消息的长度
int MPI_Get_count(MPI_Status status, MPI_Datatype datatype, int* count){} /* * status: 接收操作返回值 * datatype: 接收数据类型 * count: 接收消息的实际元素个数 * **/
非阻塞发送与接收
int MPI_Isend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request){}
/*
* buf: 接收缓存区的首地址
* count: 需要发送的字节数
* datatype: 每个发送元素的数据类型
* dest: 目标的rank(id)
* tag: 消息标识
* comm: 通信域
* request: 非阻塞通信完成对象(句柄)
**/
int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request){}
/*
* buf: 接收缓存区的首地址
* count: 接收的字节数
* datatype: 接收元素的数据类型
* source: 目标的rank(id)
* tag: 消息标识
* comm: 通信域
* request: 非阻塞通信完成对象(句柄)
**/
阻塞通信与非阻塞通信
集合通信(Collective Communication)
- 特点
- 通信空间所有进程都参与通信操作
- 每一个进程都需要调用该函数
- 形式
- 一对多
- 多对一
- 同步
通信函数
类型 | 函数 | 功能 |
---|---|---|
数据移动 | MPI_Bcast | 一到多, 数据广播 |
MPI_Gather | 多到一, 数据汇合 | |
MPI_Gatherv | ||
MPI_Allgather | ||
MPI_Allgatherv | ||
MPI_Scatter | 一到多, 数据分散 | |
MPI_Scatterv | ||
MPI_Alltoall | 多到多, 置换数据(全交换) | |
MPI_Alltoallv | ||
数据聚集 | MPI_Reduce | 多到一, 数据归约 |
MPI_Allreduce | ||
MPI_Reduce_scatter | 结果 scatter 到每个进程 | |
MPI_Scan | 前缀操作 | |
同步 | MPI_Barrier | 同步操作 |
下一篇: Java多态学习