MPI是一个跨语言的通讯协议,用于编写并行计算机。支持点对点和广播。MPI是一个信息传递应用程序接口,包括协议和和语义说明,他们指明其如何在各种实现中发挥其特性。MPI的目标是高性能,大规模性,和可移植性。MPI在今天仍为高性能计算的主要模型。
1.MPI初始函数
头文件: mpi.h/mpif.h.
int MPI_Init(int *argc, char ***argv)
启动MPI环境,标志并行代码的开始.
并行代码之前,第一个mpi函数(除MPI_Initialize()外).
要求main必须带能运行,否则出错.
通信子(通信空间): MPI_COMM_WORLD:
一个通信空间是一个进程组和一个上下文的组合.上下文可看作为组的超级标签,用于区分不同的通信子.在执行函数MPI_Init之后,一个MPI程序的所有进程形成一个缺省的组,这个组的通信子被写作MPI_COMM_WORLD.该参数是MPI通信操作函数中必不可少的参数,用于限定参加通信的进程的范围.
int MPI_Comm_size ( MPI_Comm comm, int *size )
获得通信空间comm中规定的组包含的进程的数量.
指定一个communicator,也指定了一组共享该空间的进程, 这些进程组成该communicator的group.
int MPI_Comm_rank ( MPI_Comm comm, int *rank )
得到本进程在通信空间中的rank值,即在组中的逻辑编号(从0开始).
int MPI_Finalize()
标志并行代码的结束,结束除主进程外其它进程.
之后串行代码仍可在主进程(rank = 0)上运行(如果必须).
2.点对点通信
1.阻塞通信
1.1 标准通信模式(MPI_Send)
理论上要求接收进程的recv调用配合
1.2 缓冲通信模式(MPI_Bsend)
缓冲通信模式主要用于解开阻塞通信的发送与接收之间的耦合。有了缓冲机制,即使在接收端没有启动相应的接收的情况下,在完成其消息数据到缓冲区的转移后发送端的阻塞发送函数也可返回。
1.3 就绪通信模式(MPI_Rsend)
仅当对方的接收操作启动并准备就绪时,才可发送数据。否则可能导致错误或无法预知的结果。
1.4 同步通信模式(MPI_Ssend)
不论接收端是否启动了接收动作,发送端都可在任何时机启动发送动作。但发送端需等待接收端的接收动作发起并开始接收数据之后才可能结束。
2.非阻塞通信:把计算和通信重叠起来,从而改进并行效率
2.1 非重复的非阻塞通信(MPI_Isend,MPI_IBsend,MPI_IRsend,MPI_ISsend)
2.2 可重复的非阻塞通信(MPI_Send_Init,MPI_Bsend_Init,MPI_Rsend_Init,MPI_Ssend_Init)
2.3 通信结束测试(MPI_Wait,MPI_Test等)
2.4 Probe和Cancel
MPI_Probe和MPI_IProbe可在不实际接收消息的情况下检查消息中包含的信息,然后据此决定接收消息的具体方式。
MPI_Cancel用于取消等待状态的非阻塞操作(发送或接收)。
3.组合发送接收:MPI_Sendrecv
4.点对点通信总结:
各个模式使用缓冲的特点可总结为:标准的Send实际利用MPI环境提供的默认缓冲区;Bsend实际相当于将MPI环境提供的buffer放在用户空间管理;Rsend实际相当于不要缓冲区,但发送端不能提前等待;Ssend实际相当于不要缓冲区,但允许等待。异步方式下各个模式工作原理类似,只不过将其理解为MPI环境会另起一个线程在后台做实际的消息传输,通过MPI_Waitxxx,MPI_Testxxx等机制与MPI进程的主线程进行通信和同步。
3.例子(send_init.c)
#include "mpi.h" #include <stdlib.h> #include <stdio.h> #define BUFSIZE 5 int main(int argc,char *argv[]){ MPI_Request r; MPI_Status s; int flag; int buf[BUFSIZE]; char pstr[BUFSIZE*(sizeof(int)+8)+50]; int tag=123; int dest=0; int rank,size,i,j; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&size); MPI_Comm_rank(MPI_COMM_WORLD,&rank); for(i=0;i<BUFSIZE;i++){ buf[i]=BUFSIZE*rank+1; } MPI_Send_init(buf,BUFSIZE,MPI_INT,dest,tag,MPI_COMM_WORLD,&r); if(rank==0){ int rbuf1[BUFSIZE*size]; MPI_Request *rr=(MPI_Request *)malloc(size*sizeof(MPI_Request)); for(i=0;i<size;i++){ fprintf(stdout,"proc:%d,before Irecv..\n",rank); MPI_Irecv(rbuf1+i*BUFSIZE,BUFSIZE,MPI_INT,i,tag,MPI_COMM_WORLD,&rr[i]); fprintf(stdout,"proc:%d,after Irecv..\n",rank); } MPI_Start(&r); MPI_Wait(&r,&s); MPI_Waitall(size,rr,MPI_STATUSES_IGNORE); for(i=0;i<size;i++){ sprintf(pstr,"proc:%d received message from %d\n",rank,i); for(j=0;j<BUFSIZE-1;j++){ sprintf(pstr,"%srbuf1[%d]=%d,",pstr,i*BUFSIZE+j,rbuf1[i*BUFSIZE+j]); } sprintf(pstr,"%srbuf1[%d]=%d\n",pstr,i*BUFSIZE+j,rbuf1[i*BUFSIZE+j]); fprintf(stdout,"%s",pstr); } free(rr); }else{ MPI_Start(&r); MPI_Wait(&r,&s); } MPI_Request_free(&r); if(rank==0){ MPI_Request sr; int rbuf2[BUFSIZE]; MPI_Recv_init(rbuf2,BUFSIZE,MPI_INT,MPI_ANY_SOURCE,tag,MPI_COMM_WORLD,&r); fprintf(stdout,"proc: %d,before Isend...\n",rank); MPI_Isend(buf,BUFSIZE,MPI_INT,0,tag,MPI_COMM_WORLD,&sr); fprintf(stdout,"proc: %d,after Isend...\n",rank); for(i=0;i<size;i++){ MPI_Start(&r); MPI_Wait(&r,&s); sprintf(pstr,"proc: %d received messages from:%d\n",rank,s.MPI_SOURCE); for(j=0;j<BUFSIZE-1;j++){ sprintf(pstr,"%srbuf2[%d]=%d,",pstr,j,rbuf2[j]); } sprintf(pstr,"%srbuf2[%d]=%d\n",pstr,j,rbuf2[j]); fprintf(stdout,"%s",pstr); } MPI_Wait(&sr,&s); MPI_Request_free(&r); }else{ fprintf(stdout,"proc: d,before Send....\n",rank); MPI_Send(buf,BUFSIZE,MPI_INT,0,tag,MPI_COMM_WORLD); fprintf(stdout,"proc: %d,after Send....\n",rank); } MPI_Finalize(); return 0; }
Linux下编译运行指令:
mpicc send_init.c -o send_init
mpirun -np 8 ./send_init