(一)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);
(三)计时功能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);
标签:int,void,MPI,Comm,进程,size From: https://www.cnblogs.com/imreW/p/17181853.html