MPI
(一)MPI简介
MPI是一个跨语言的通讯协议,用于编写并行计算机。支持点对点和广播。MPI是一个信息传递应用程序接口,包括协议和和语义说明,他们指明其如何在各种实现中发挥其特性。
MPI的目标是高性能,大规模性,和可移植性。MPI在今天仍为高性能计算的主要模型。与OpenMP并行程序不同,MPI是一种基于信息传递的并行编程技术。
消息传递接口是一种编程接口标准,而不是一种具体的编程语言。简而言之,MPI标准定义了一组具有可移植性的编程接口。
(二)MPI基本函数
1.MPI_Init
int MPI_Init(int*argc, char *argv[])
在C接口中,MPI系统通过argc和argv得到命令行参数,并且会把MPI系统专用的参数删除,留下用户的解释参数。
该函数通常应该是第一个被调用的MPI函数用于并行环境初始化,其后面的代码到 MPI_Finalize()函数之前的代码在每个进程中都会被执行一次。
– 除MPI_Initialized()外, 其余所有的MPI函数应该在其后被调用。
– MPI系统将通过argc,argv得到命令行参数(也就是说main函数必须带参数,否则会出错)。
例子:
MPI_Init (&argc, &argv);
2.MPI_Comm_size
int MPI_Comm_size(MPI_Comm comm, int *size) IN comm 通信域 句柄 OUT size 通信域comm内包括的进程数 整数
– 获得进程个数 size。
– 指定一个通信子,也指定了一组共享该空间的进程, 这些进程组成该通信子的group(组)。
– 获得通信子comm中规定的group包含的进程的数量。
例子:
MPI_Comm_size (MPI_COMM_WORLD, &size);
3.MPI_Comm_rank
int MPI_Comm_rank(MPI_Comm comm, int *rank) IN comm 该进程所在的通信域 句柄 OUT rank 调用进程在comm中的标识号
当MPI初始化后,每一个活动进程变成了一个叫MPI_COMM_WORLD的通信域中的成员。一个通信域是一个不透明的对象,提供了在进程之间传递消息的环境。
在一个通信域内的进程是有序的,一个进程的序号便是它在整个排序中的位置。在一个有p个进程的通信域中,每一个进程有一个唯一的序号(ID号),取值为0~p-1。
可以使用进程的序号来决定它负责计算和(或)数据集的哪一部分,进程可以通过调用函数MPI_Comm_rank来确定它在通信域中的序号
– 得到本进程在通信空间中的rank值,即在组中的逻辑编号(该 rank值为0到p-1间的整数,相当于进程的ID。
例子:
MPI_Comm_rank (MPI_COMM_WORLD, &rank);
4.MPI_Send
int MPI_Send( void *buf, //buf为消息的地址 int count,//count是内容的数量 MPI_Datatype datatype, //datatype为消息内容的数据类型 int goal, //goal为目标进程编号 int tag, //tag为消息的标志 MPI_Comm comm//comm为通信域 )
5.MPI_Recv
int MPI_Recv( void *buf, //接收消息的内存地址 int count, //你接收消息的消息的个数(注意:不是长度,例如你要发送一个int整数,这里就填写1,如要是发送“hello”字符串,这里就填写6(C语言中字符串未有一个结束符,需要多一位))。 //它是接收数据长度的上界. 具体接收到的数据长度可通过调用MPI_Get_count 函数得到。 MPI_Datatype datatype,//你要接收的数据类型,这里需要用MPI定义的数据类型,可在网上找到,在此不再罗列。 int source, //消息源地址 可以为MPI_ANY_SOURCE 代表任何源地址都接受 int tag, //消息标签 可以为MPI_ANY_TAG 代表任意标签都接受 MPI_Comm comm, MPI_Status *status//消息接收状态 可以为MPI_STATUS_IGNORE //消息状态。接收函数返回时,将在这个参数指示的变量中存放实际接收消息的状态信息,包括消息的源进程标识,消息标签,包含的数据项个数等。 )
6.MPI_Finalize
int MPI_Finalize (void)
如果 函数成功返回,MPI_SUCCESS。 如果由于其他原因调用失败, ((如无效参数) )而失败,则其他错误代码。
– 退出MPI系统, 所有进程正常退出都必须调用。 表明并行代码的结束,结束除主进程外其它进程。
– 串行代码仍可在主进程(rank = 0)上运行, 但不能再有MPI函数(包括MPI_Init())。
在调用 MPI_Init 或 MPI_Init_thread的线程上退出之前,所有 MPI 进程都必须调用此例程。
MPI_Finalize函数清理与 MPI 相关的所有状态。 调用后,无法调用其他 MPI 函数,包括 MPI_Init 和 MPI_Init_thread。 应用程序必须确保在调用 MPI_Finalize之前完成或取消所有挂起的通信。
例子:
MPI_Finalize()不会结束任何进程
int main() { MPI_Init(NULL, NULL); printf("Hello world in MPI\n"); MPI_Finalize(); printf("Hello world out of MPI\n"); return 0; }
N表示开了N个进程,所以会有N个Hello world in MPI
但是在MPI_Finalize()之后的Hello world out of MPI也会有N个
(三)计时功能MPI_Wtime
double MPI_Wtime(void)
获取墙上时间,返回调用时刻的墙上时间,用浮点数表示秒数;经常用来计算程序运行时间。
例子:
// 开始记录时间 begin = MPI_Wtime ();
(四)MPI函数
1.MPI_Scatter
int MPI_Scatter( const void *sendbuf, //存储在0号进程的数据,array int sendcount, //具体需要给每个进程发送的数据的个数 //如果send_count为1,那么每个进程接收1个数据;如果为2,那么每个进程接收2个数据 MPI_Datatype sendtype,//发送数据的类型 void *recvbuf, //接收的数据要存放的地址 int recvcount, //接收的数据量 MPI_ Datatype recvtype,//接收的数据数据类型。 int root,//root进程的编号这个参数说明的是你的根线程,因为你必须要有一个线程来掌控全局,来进行数据的分发,一般都使用线程0 MPI_Comm comm);//MPI_COMM_WORLD
MPI_Scatter与MPI_Bcast非常相似,都是一对多的通信方式,不同的是后者的0号进程将相同的信息发送给所有的进程,而前者则是将一段array 的不同部分发送给所有的进程,其区别可以用下图概括:
0号进程分发数据的时候是根据进程的编号进行的,array中的第一个元素发送给0号进程,第二个元素则发送给1号进程,以此类推。
例子:
MPI_Scatter (a_all, n_per, MPI_LONG, a, n_per, MPI_LONG, 0, MPI_COMM_WORLD);//分发数据
2.MPI_Gather
MPI_Gather( void* send_data,//数据发送缓存地址 int send_count,//数据个数 MPI_Datatype send_datatype,//数据类型 void* recv_data,//数据接收缓存地址 int recv_count,//注意该参数表示的是从单个进程接收的数据个数,不是总数 MPI_Datatype recv_datatype,//数据类型 int root,//根进程标识 MPI_Comm communicator)//通信域
MPI_Gather和MPI_scatter刚好相反,他的作用是从所有的进程中将每个进程的数据集中到根进程中,同样根据进程的编号对array元素排序,如图所示:
例子:
MPI_Gather (samples, size, MPI_LONG, samples_all, size, MPI_LONG, 0, MPI_COMM_WORLD);
3.MPI_Gatherv
int MPI_Gatherv( void* sendbuf, //IN发送消息缓冲区的起始地址(可变) int sendcount, //IN发送消息缓冲区中的数据个数(整型) MPI_Datatype sendtype, //IN发送消息缓冲区中的数据类型(句柄) void* recvbuf, //OUT接收消息缓冲区的起始地址(可变,仅对于根进程) int *recvcounts, //IN整型数组(长度为组的大小), 其值为从每个进程接收的数据个数(仅对于根进程) int *displs, //IN整数数组,每个入口i表示相对于recvbuf的位移,此位移处存放着从进程i中接收的输入数据(仅对于根进程) MPI_Datatype recvtype, //IN接收消息缓冲区中数据类型(仅对于根进程)(句柄) int root, // IN接收进程的序列号(句柄) MPI_Comm comm)//IN通信子(句柄)
它可以从不同的进程接收不同数量的数据
例子:
MPI_Gatherv (new_partitions, totalsize, MPI_LONG, a_all, recv_count, recv_dis, MPI_LONG, 0, MPI_COMM_WORLD);
4.MPI_Bcast
int MPI_Bcast( void* buffer,//IN/OUT通信消息缓冲区的起始地址(可变) int count,//IN通信消息缓冲区中的数据个数(整型) MPI_Datatype datatype,//IN通信消息缓冲区中的数据类型(句柄) int root, //IN发送广播的根的序列号(整型) MPI_Comm comm) //IN通信子(句柄)
MPI_Bcast用于将一个进程的buffer中的数据广播到其他进程的相同buffer变量中
例子:
MPI_Bcast (pivots, (size - 1), MPI_LONG, 0, MPI_COMM_WORLD);
5.MPI_Alltoall
int MPI_Alltoall( const void *sendbuf,//发送缓冲区的起始地址 int sendcount,//发送的数量 MPI_Datatype sendtype,//发送的数据类型 void *recvbuf, //接收缓冲区的起始位置 int recvcount, //要接收的数量 MPI_Datatype recvtype,//要接收的类型 MPI_Comm comm)//通信子
这个是指当前进程往其他每个进程(包括自己)要发送的数据都是一样的,都是发送sendbuf中的数据。
rank send buf recv buf ---- -------- -------- 0 a,b,c MPI_Alltoall a,A,# 1 A,B,C ----------------> b,B,@ 2 #,@,% c,C,% (a more elaborate case with two elements per process) rank send buf recv buf ---- -------- -------- 0 a,b,c,d,e,f MPI_Alltoall a,b,A,B,#,@ 1 A,B,C,D,E,F ----------------> c,d,C,D,%,$ 2 #,@,%,$,&,* e,f,E,F,&,*
例子:
MPI_Alltoall (partition_size, 1, MPI_INT, new_partition_size, 1, MPI_INT, MPI_COMM_WORLD);
6.MPI_Alltoallv
有时候,我们当前进程往其他进程发送的数据不一样,个数也不一样,这个时候就需要用MPI_Alltoallv来解决。
int MPI_Alltoallv( const void *sendbuf,//发送缓冲区的起始地址 const int *sendcounts, //可以把当做数组,数组中的元素代表往其他节点各发送多少数据。 const int *sdispls,//看做是数组,数组中的每个元素代表了要发送的那块数据相对于缓冲区起始位置的位移量。 MPI_Datatype sendtype,//发送的数据类型 void *recvbuf,//接收缓冲区的起始位置 const int *recvcounts, //可以把当做数组,数组中的元素代表往其他节点各j接收多少数据。 const int *rdispls,//看做是数组,数组中的每个元素代表了要接收的那块数据相对于缓冲区起始位置的位移量。 MPI_Datatype recvtype,//要接收的类型 MPI_Comm comm)//通信子
比如说,sendcounts[0]=3,sendcounts[1]=4,代表该节点要往0号节点发送3个sendtype的数据,往1号节点发送4个sendtype的数据。
例子:
MPI_Alltoallv (a, partition_size, send_dis, MPI_LONG, new_partitions, new_partition_size, recv_dis, MPI_LONG, MPI_COMM_WORLD);
7.MPI_Barrier
MPI_Barrier(MPI_COMM_WORLD);
MPI_BARRIER阻塞所有的调用者直到所有的组成员都调用了它,各个进程中这个调用才可以返回。就是说,有些进程执行得快,有些进程执行得慢,要等待所有的进程都执行到这里,才开时同时执行之后的命令,即“同步”。
例子:
MPI_Barrier (MPI_COMM_WORLD);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了