【MPI学习3】MPI并行程序设计模式:不同通信模式MPI并行程序的设计
2016-02-20 22:39
597 查看
学习了MPI四种通信模式 及其函数用法:
(1)标准通信模式:MPI_SEND
(2)缓存通信模式:MPI_BSEND
(3)同步通信模式:MPI_SSEND
(4)就绪通信模式:MPI_RSEND
四种通信模式的区别都在消息发送端,而消息接收端的操作都是MPI_RECV。
1.标准通信模式
原理图如下
标准通信模式由MPI决定是否用缓存。
如果MPI决定缓存将要发出的数据:发送操作不管接受操作是否执行,都可以进行;而且缓存结束后发送操作就可以返回,不需要等待接受操作收到数据
如果MPI决定不缓存将要发送的数据:对于阻塞通信,则要求接受操作执行,并且数据都发送到接受缓冲区了,发送操作才能够返回;对于非阻塞通信,发送操作虽然没有完成,但是发送调用可以正确返回。
2.缓存通信模式
与标准通信的区别在于需要自己维护程序的缓冲区。
int MPI_Buffer_attach(void *buffer, int size)用于申请缓存
int MPI_Buffer_detach(void **buffer, int *size) 用于释放缓存 这是一个阻塞调用 函数返回表示缓冲区已经被释放
示例代码如下:
代码输出结果是:
总共需要发送5个double类型,每个类型占8个字节;MPI通信其他附属信息占200个字节;因此总共缓冲区的大小的240个字节。
3.同步通信模式
同步发送进程的特点是:发送操作可以不依赖接受进程的相应接受操作是否已经启动,但是必须等着接受操作开始执行后才能返回;这意味着一旦同步发送返回后,发送缓冲区中的数据已经全部被系统缓冲区缓存。“发送缓冲区”表示MPI的缓冲区,“系统缓冲区”指的是操作系统的写缓冲区,注意二者的区别。这意味着同步发送缓冲区中的数据可以被释放或重新利用。而标准通信模式(或缓存通信模式)中,在用缓存的模式下,发送操作返回仅仅意味着数据都已经到发送缓冲区中了,数据是否到系统缓冲区不得知。
示例代码如下:
代码执行结果如下:
如果将33 34行代码互换位置,则程序陷入了deadlock:一方面发送进程中tag=1的MPI_Ssend操作一直处于阻塞状态;另一方面接受进程中的tag=2的MPI_Recv操作处于阻塞状态;两个进程互相等着对方,陷入了死锁。
4. 就绪通信模式
与前几种通信模式不同,只有当接受进程的接受操作已经启动时,才可以在发送端启动发送进程。
一种可能的就绪通信模式实现方法如下图:
上图保证的目标是①要先于④执行;方法是插入②和③;①一定先于②执行,③一定等②成功后才能执行,③成功后④才能执行。
由此一定保证①先于④执行,就保证了就绪通信。
示例代码如下:
代码执行结果如下:
(1)标准通信模式:MPI_SEND
(2)缓存通信模式:MPI_BSEND
(3)同步通信模式:MPI_SSEND
(4)就绪通信模式:MPI_RSEND
四种通信模式的区别都在消息发送端,而消息接收端的操作都是MPI_RECV。
1.标准通信模式
原理图如下
标准通信模式由MPI决定是否用缓存。
如果MPI决定缓存将要发出的数据:发送操作不管接受操作是否执行,都可以进行;而且缓存结束后发送操作就可以返回,不需要等待接受操作收到数据
如果MPI决定不缓存将要发送的数据:对于阻塞通信,则要求接受操作执行,并且数据都发送到接受缓冲区了,发送操作才能够返回;对于非阻塞通信,发送操作虽然没有完成,但是发送调用可以正确返回。
2.缓存通信模式
与标准通信的区别在于需要自己维护程序的缓冲区。
int MPI_Buffer_attach(void *buffer, int size)用于申请缓存
int MPI_Buffer_detach(void **buffer, int *size) 用于释放缓存 这是一个阻塞调用 函数返回表示缓冲区已经被释放
示例代码如下:
#include "mpi.h" #include <stdio.h> #include <stdlib.h> #define SIZE 6 static int src = 0; static int dest = 1; void generate_data(double *, int); void normal_recv(double *, int); void buffered_send(double *, int); void generate_data(double *buffer, int buff_size){ int i; for (i=0; i<buff_size; i++) buffer[i]=(double)i+1; } void normal_recv(double *buffer, int buff_size){ int i,j; MPI_Status status; double *b; b = buffer; MPI_Recv(b,(buff_size-1),MPI_DOUBLE,src,2000,MPI_COMM_WORLD, &status); fprintf(stderr, "standard receive a message of %d data\n", buff_size-1); for(j=0; j<buff_size-1; j++) fprintf(stderr, "buf[%d]=%f\n",j,b[j]); b+=buff_size-1; MPI_Recv(b, 1, MPI_DOUBLE, src, 2000, MPI_COMM_WORLD, &status); fprintf(stderr, "standard receive a message of one data\n"); fprintf(stderr, "buf[0]=%f\n",*b); } void buffered_send(double *buffer, int buff_size){ int i,j; void *bbuffer; int size; fprintf(stderr, "buffered send message of %d data\n", buff_size-1); for(j=0; j<buff_size-1; j++) fprintf(stderr, "buf[%d]=%f\n",j,buffer[j]); MPI_Bsend(buffer, (buff_size-1), MPI_DOUBLE, dest, 2000, MPI_COMM_WORLD); buffer+=buff_size-1; fprintf(stderr, "bufferred send message of one data\n"); fprintf(stderr, "buf[0]=%f\n", *buffer); MPI_Bsend(buffer, 1, MPI_DOUBLE, dest, 2000, MPI_COMM_WORLD); MPI_Buffer_detach(&buffer, &size); MPI_Buffer_attach(bbuffer, size); } int main(int argc, char *argv[]) { int rank; double buffer[SIZE], *tmpbuffer, *tmpbuf; int tsize, bsize; char *test = NULL; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank==src) { // 发送消息进程 generate_data(buffer, SIZE); MPI_Pack_size(SIZE, MPI_DOUBLE, MPI_COMM_WORLD, &bsize); tmpbuffer = (double*)malloc(bsize+2*MPI_BSEND_OVERHEAD); if (!tmpbuffer) { MPI_Abort(MPI_COMM_WORLD, 1); } // 告诉系统MPI_Bsend用到buffer就去tmpbuffer那里去找 MPI_Buffer_attach(tmpbuffer, bsize+2*MPI_BSEND_OVERHEAD); buffered_send(buffer, SIZE); MPI_Buffer_detach(&tmpbuf, &tsize); printf("tsize detach from tmpbuf is : %d\n", tsize); } else if (rank==dest) { normal_recv(buffer, SIZE); } else { MPI_Abort(MPI_COMM_WORLD, 1); } MPI_Finalize(); }
代码输出结果是:
总共需要发送5个double类型,每个类型占8个字节;MPI通信其他附属信息占200个字节;因此总共缓冲区的大小的240个字节。
3.同步通信模式
同步发送进程的特点是:发送操作可以不依赖接受进程的相应接受操作是否已经启动,但是必须等着接受操作开始执行后才能返回;这意味着一旦同步发送返回后,发送缓冲区中的数据已经全部被系统缓冲区缓存。“发送缓冲区”表示MPI的缓冲区,“系统缓冲区”指的是操作系统的写缓冲区,注意二者的区别。这意味着同步发送缓冲区中的数据可以被释放或重新利用。而标准通信模式(或缓存通信模式)中,在用缓存的模式下,发送操作返回仅仅意味着数据都已经到发送缓冲区中了,数据是否到系统缓冲区不得知。
示例代码如下:
#include "mpi.h" #include <stdio.h> #define SIZE 10 static int src = 0; static int dest = 1; int main(int argc, char *argv[]) { int rank; int act_size = 0; int flag, np, rval, i; int buffer[SIZE]; MPI_Status status, status1, status2; int count1, count2; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &np); if (np!=2) { MPI_Abort(MPI_COMM_WORLD, 1); } act_size = 5; /*最大消息长度*/ if (rank==src) { MPI_Ssend(buffer, act_size, MPI_INT, dest, 1, MPI_COMM_WORLD); fprintf(stderr, "MPI_Ssend %d data,tag=1\n", act_size); act_size = 4; MPI_Ssend(buffer, act_size, MPI_INT, dest, 2, MPI_COMM_WORLD); fprintf(stderr, "MPI_Ssend %d data,tag=2\n", act_size); } else if (rank=dest) { MPI_Recv(buffer, act_size, MPI_INT, src, 1, MPI_COMM_WORLD, &status1); MPI_Recv(buffer, act_size, MPI_INT, src, 2, MPI_COMM_WORLD, &status2); MPI_Get_count(&status1, MPI_INT, &count1); fprintf(stderr, "receive %d data,tag=%d\n",count1, status1.MPI_TAG); MPI_Get_count(&status2, MPI_INT, &count2); fprintf(stderr, "receive %d data,tag=%d\n",count2, status2.MPI_TAG); } MPI_Finalize(); }
代码执行结果如下:
如果将33 34行代码互换位置,则程序陷入了deadlock:一方面发送进程中tag=1的MPI_Ssend操作一直处于阻塞状态;另一方面接受进程中的tag=2的MPI_Recv操作处于阻塞状态;两个进程互相等着对方,陷入了死锁。
4. 就绪通信模式
与前几种通信模式不同,只有当接受进程的接受操作已经启动时,才可以在发送端启动发送进程。
一种可能的就绪通信模式实现方法如下图:
上图保证的目标是①要先于④执行;方法是插入②和③;①一定先于②执行,③一定等②成功后才能执行,③成功后④才能执行。
由此一定保证①先于④执行,就保证了就绪通信。
示例代码如下:
#include "mpi.h" #include <stdio.h> #include <stdlib.h> #define TEST_SIZE 2000 void test_rsend(); int main(int argc, char *argv[]) { MPI_Init(&argc, &argv); test_rsend(); MPI_Finalize(); } void test_rsend() { int rank, size; int next, prev; int tag; int count; float send_buf[TEST_SIZE], recv_buf[TEST_SIZE]; MPI_Status status; MPI_Request request; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); if (2!=size) { MPI_Abort(MPI_COMM_WORLD, 1); } next = rank + 1; if (next >= size) next = 0; prev = rank - 1; if (prev <0) prev = size-1; if (0==rank) { fprintf(stderr, " Rsend Test\n"); } tag = 1456; count = TEST_SIZE/3; if (0==rank) { MPI_Recv(MPI_BOTTOM,0,MPI_INT,next,tag,MPI_COMM_WORLD, &status); fprintf(stderr, " Process %d post Ready send\n", rank); MPI_Rsend(send_buf,count,MPI_FLOAT,next,tag,MPI_COMM_WORLD); } else { fprintf(stderr, " process %d post a receive call\n", rank); MPI_Irecv(recv_buf, TEST_SIZE, MPI_FLOAT,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&request); MPI_Send(MPI_BOTTOM,0,MPI_INT,next,tag,MPI_COMM_WORLD); MPI_Wait(&request, &status); fprintf(stderr, " Process %d Receive Rsend message from %d\n",rank, status.MPI_SOURCE); } }
代码执行结果如下:
相关文章推荐
- HDU 5630 Rikka with Chess
- oracle之函数使用大全
- java学习笔记-参数的调用
- JSP-include指令
- UVA 12658 模拟
- neuq oj 1047: 谭浩强C语言(第三版)习题6.3 C++
- PyQt4入门教程(3)_菜单栏和工具条
- 第三百二十四天 how can I 坚持
- 在文章中使用amsmath宏包的原因
- VS2010~VS2013中文注释带红色下划线的解决方法
- 【C#】基础知识—初识C#与.Net
- POJ 1013 Counterfeit Dollar
- 积跬步至千里系列之十--编译Android源码实践
- 向量
- jQuery单页应用无刷新插件
- Nginx的源码安装
- VS2015--win32工程配置的一些想法之warning LNK4042: 对象被多次指定;已忽略多余的指定
- Experimental Educational Round: VolBIT Formulas Blitz(J)打表找规律
- vs2015 + BabeLua + Cocos2d-x 3.10配置
- VS2015--win32工程配置的一些想法之warning LNK4042: 对象被多次指定;已忽略多余的指定