MPI学习三 不同通信模式MPI并行程序的设计

MPI四种通信模式及其函数:

  1. 标准通信模式:MPI_SEND
  2. 缓存通信模式:MPI_BSEND
  3. 同步通信模式:MPI_SSEND
  4. 就绪通信模式:MPI_RSEND

四种通信模式的区别都在消息发送端,而消息接收端的操作都是MPI_RECV。

1. 标准通信模式

标准通信模式下,是否对发送的数据进行缓存是由MPI自身决定的,而不是由并行程序员来控制。 
如果MPI不缓存将要发送的数据:对于阻塞通信,只有当相应的接收调用被执行后,并且发送数据完全到达接收缓冲区后,发送操作才算完成。对于非阻塞通信,发送操作虽然没有完成,但是发送调用可以正确返回,程序可以接下来执行其它的操作。 
如果MPI缓存将要发出的数据:发送操作不管接收操作是否执行,都可以进行 而,且发送操作可以正确返回而不要求接收操作收到发送的数据。

2. 缓存通信模式

缓存通信模式下,由用户直接对通信缓冲区进行申请、使用和释放。对通信缓冲区的合理与正确使用是由程序设计人员自己保证的。

int MPI_Bsend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)

缓存通信模式不管接收操作是否启动,发送操作都可以执行。 
采用缓存通信模式时,消息发送能否进行及能否正确返回不依赖于接收进程,完全依赖于是否有足够的通信缓冲区可用。对于非阻塞发送,正确退出并不意味者缓冲区可以被其它的操作任意使用,对于阻塞发送返回后其缓冲区是可以重用的。

int MPI_Buffer_attach(void *buffer, int size) //用于申请缓存 
int MPI_Buffer_detach(void **buffer, int *size) //用于释放缓存

释放缓存是阻塞调用,它一直等到使用该缓存的消息发送完成后才返回,这一调用返回后用户可以重新使用该缓冲区或者将这一缓冲区释放。

 
  
  1. #include "mpi.h"
  2. #include
  3. #include
  4. void main(int argc, char *argv[])
  5. {
  6. int rank,size;
  7. MPI_Status status;
  8. double buffer[6], *tmpbuffer, *tmpbuf;
  9. int tsize, bsize;
  10. int SIZE = 6;
  11. int src = 0;
  12. int dest = 1;
  13. int i,j;
  14. MPI_Init(&argc, &argv);
  15. MPI_Comm_rank(MPI_COMM_WORLD, &rank); /* 0 or 1 */
  16. MPI_Comm_size(MPI_COMM_WORLD,&size);
  17. if (size != 2)
  18. {
  19. fprintf(stderr, "*** This program uses exactly 2 processes! ***\n");
  20. MPI_Abort( MPI_COMM_WORLD, 1 );
  21. }
  22. if (rank == src) //当前进程为发送进程
  23. {
  24. for (i = 0; i < SIZE; i++) //产生发送数据
  25. buffer[i] = (double)i+1;
  26. //计算发送SIZE个MPI_DOUBLE类型的数据需要多大空间
  27. MPI_Pack_size( SIZE, MPI_DOUBLE, MPI_COMM_WORLD, &bsize );
  28. //申请缓存发送所需要的空间,注意MPI_BSEND_OVERHEAD
  29. tmpbuffer = (double *) malloc( bsize + 2*MPI_BSEND_OVERHEAD );
  30. if (!tmpbuffer)
  31. {
  32. fprintf( stderr, "Could not allocate bsend buffer of size %d\n", bsize );
  33. MPI_Abort( MPI_COMM_WORLD, 1 );
  34. }
  35. //将申请的空间递交给MPI
  36. MPI_Buffer_attach( tmpbuffer, bsize + MPI_BSEND_OVERHEAD );
  37. fprintf(stderr,"buffered send message of %d data\n",SIZE);
  38. for (j=0;j<SIZE;j++)
  39. fprintf(stderr,"buf[%d]=%f\n",j,buffer[j]);
  40. //MPI执行缓存模式发送
  41. MPI_Bsend(buffer, SIZE, MPI_DOUBLE, dest, 2000, MPI_COMM_WORLD);
  42. //发送完成后收回递交的缓存区
  43. MPI_Buffer_detach( &tmpbuf, &tsize );
  44. }
  45. else if (rank == dest) //当前为接收进程
  46. {
  47. MPI_Recv(buffer, SIZE, MPI_DOUBLE, src, 2000, MPI_COMM_WORLD, &status); //标准模式消息接收
  48. fprintf(stderr,"standard receive a message of %d data\n",SIZE);
  49. for (j=0;j<SIZE;j++)
  50. fprintf(stderr," buf[%d]=%f\n",j,buffer[j]);
  51. }
  52. MPI_Finalize();
  53. }

运行结果

[root@dl1 mpi]#  mpirun -np 2 ./test-9-2
buffered send message of 6 data
buf[0]=1.000000
buf[1]=2.000000
buf[2]=3.000000
buf[3]=4.000000
buf[4]=5.000000
buf[5]=6.000000
standard receive a message of 6 data
 buf[0]=1.000000
 buf[1]=2.000000
 buf[2]=3.000000
 buf[3]=4.000000
 buf[4]=5.000000
 buf[5]=6.000000

3. 同步通信模式

同步通信模式的开始不依赖于接收进程相应的接收操作是否已经启动,但是同步发送却必须等到相应的接收进程开始后才可以正确返回。因此,同步发送返回后,意味着发送缓冲区中的数据已经全部被系统缓冲区缓存,并且已经开始发送。这样当同步发送返回后,发送缓冲区可以被释放或重新使用。

 
  
  1. #include
  2. #include "mpi.h"
  3. #define SIZE 10
  4. int main( int argc, char *argv[])
  5. {
  6. int rank, size;
  7. int act_size = 0;
  8. int flag, rval, i;
  9. int buffer[SIZE];
  10. MPI_Status status, status1, status2;
  11. int count1, count2;
  12. int src = 0;
  13. int dest = 1;
  14. MPI_Init(&argc, &argv);
  15. MPI_Comm_rank(MPI_COMM_WORLD, &rank); /* 0 or 1 */
  16. MPI_Comm_size( MPI_COMM_WORLD, &size ); /* 2 */
  17. if (size != 2)
  18. {
  19. fprintf(stderr, "*** This program uses exactly 2 processes! ***\n");
  20. MPI_Abort( MPI_COMM_WORLD, 1 );
  21. }
  22. act_size = 5;/*最大消息长度*/
  23. if (rank == src) /*当前进程为发送进程*/
  24. {
  25. act_size = 1;
  26. MPI_Ssend( buffer, act_size, MPI_INT, dest, 1, MPI_COMM_WORLD );/*同步消息发送 发送一个整型数 tag标识为1*/
  27. fprintf(stderr,"MPI_Ssend %d data,tag=1\n", act_size);
  28. act_size = 4;
  29. MPI_Ssend( buffer, act_size, MPI_INT, dest, 2, MPI_COMM_WORLD ); /*同步发送4个整型数 tag标识为2*/
  30. fprintf(stderr,"MPI_Ssend %d data,tag=2\n", act_size);
  31. }
  32. else if (rank == dest) /*当前进程为接收进程*/
  33. {
  34. /*标准消息接收 这里指定的消息长度act_size是最大消息长度 tag为1*/
  35. MPI_Recv( buffer, act_size, MPI_INT, src, 1, MPI_COMM_WORLD, &status1 );
  36. /*标准消息接收 这里指定的消息长度act_size是最大消息长度 tag为2*/
  37. MPI_Recv( buffer, act_size, MPI_INT, src, 2, MPI_COMM_WORLD, &status2 );
  38. MPI_Get_count( &status1, MPI_INT, &count1 );/*消息1包含的数据的个数*/
  39. fprintf(stderr,"receive %d data,tag=%d\n",count1,status1.MPI_TAG);
  40. MPI_Get_count( &status2, MPI_INT, &count2 );/*消息2包含的数据的个数*/
  41. fprintf(stderr,"receive %d data,tag=%d\n",count2,status2.MPI_TAG);
  42. }
  43. MPI_Finalize();
  44. }

运行结果

[root@dl1 mpi]#  mpirun -np 2 ./test-9-3
MPI_Ssend 1 data,tag=1
MPI_Ssend 4 data,tag=2
receive 1 data,tag=1
receive 4 data,tag=2

4. 就绪通信模式

在就绪通信模式中,只有当接收进程的接收操作已经启动时,才可以在发送进程启动发送操作。否则,当发送操作启动而相应的接收还没有启动时,发送操作将出错。对于非阻塞发送操作的正确返回,并不意味着发送已完成,但对于阻塞发送的正确返回,则发送缓冲区可以重复使用。

 
  
  1. #include "mpi.h"
  2. #include
  3. #include
  4. #define TEST_SIZE 2000
  5. int main(int argc, char *argv[])
  6. {
  7. int rank, size;
  8. int next, prev;
  9. int tag;
  10. int count;
  11. float send_buf[TEST_SIZE], recv_buf[TEST_SIZE];
  12. MPI_Status status;
  13. MPI_Request request;
  14. MPI_Init(&argc, &argv);
  15. MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  16. MPI_Comm_size(MPI_COMM_WORLD, &size);
  17. if (size != 2)
  18. {
  19. fprintf(stderr, "*** This program uses exactly 2 processes! ***\n");
  20. MPI_Abort(MPI_COMM_WORLD, 1);
  21. }
  22. next = rank + 1;
  23. if (next >= size)
  24. next = 0;
  25. prev = rank - 1;
  26. if (prev <0)
  27. prev = size-1;
  28. if (0==rank)
  29. {
  30. fprintf(stderr, " Rsend Test\n");
  31. }
  32. tag = 1456;
  33. count = TEST_SIZE/3;
  34. if (0==rank)
  35. {
  36. MPI_Recv(MPI_BOTTOM,0,MPI_INT,next,tag,MPI_COMM_WORLD, &status); //收到其接收进程通知,表示其接收操作已经启动
  37. fprintf(stderr, " Process %d post Ready send\n", rank);
  38. MPI_Rsend(send_buf,count,MPI_FLOAT,next,tag,MPI_COMM_WORLD); //执行就绪模式发送
  39. }
  40. else
  41. {
  42. fprintf(stderr, " process %d post a receive call\n", rank);
  43. MPI_Irecv(recv_buf, TEST_SIZE, MPI_FLOAT,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&request); //启动就绪模式接收
  44. MPI_Send(MPI_BOTTOM,0,MPI_INT,next,tag,MPI_COMM_WORLD); //通知发送进程接收进程的接收操作已经启动
  45. MPI_Wait(&request, &status);
  46. fprintf(stderr, " Process %d Receive Rsend message from %d\n",rank, status.MPI_SOURCE);
  47. }
  48. MPI_Finalize();
  49. }

运行结果

[root@dl1 mpi]#  mpirun -np 2 ./test-9-4
 process 1 post a receive call
 Rsend Test
 Process 0 post Ready send
 Process 1 Receive Rsend message from 0


你可能感兴趣的:(MPI)