4.2 MPI的异步通信(非阻塞收发)
-
同步通信:当一个消息发送操作完全完成(消息接收者已经收到了该消息)时,称为同步发送;同理,当一个消息接收操作完全完成(消息接收者已经收到了该消息)时,称为同步接收。上文中提到的
MPI_Send
和MPI_Recv
就是同步收发函数,或者说是阻塞的收发函数。 -
异步通信:当一个消息发送操作部分完成(不管消息接收者是否已经收到消息,我就是立即返回)时,称为异步发送;同理,当一个消息接收操作部分完成(不管是否接收到了消息,我就是立即返回)时,称为异步接收。下文中即将提到的
MPI_Isend
和MPI_Irecv
就是异步收发函数,或者说是非阻塞的收发函数。函数名 描述 MPI_Isend(void *buff,
int count,
MPI_Datatype type,
int dest,
int tag,
int comm,
MPI_Request *request )发送一个点对点的异步消息到通信组comm中的dest进程;
该函数被调用后会立即返回,并通过request输出参变量返回消息发送状态。
需要注意的是,在确认消息已经完全发送出去之前,不要改变消息缓存区buff中的内容。MPI_Recv(void *buff,
int count,
MPI_Datatype type,
int source,
int tag,
int comm,
MPI_Request *request )从通信组comm中的source进程接收一个点对点的异步消息到buff中;
该函数被调用后会立即返回,并通过request输出参变量返回消息发送状态。
需要注意的是,在确认消息已经完全接收之前,不要改变消息缓存区buff中的内容。-
当你开始了一个异步收发操作后,你暂时是不能使用缓存区中的数据,直至收发操作执行完毕!那我怎么知道收发有没有结束呢?请看下面两个函数:
函数 描述 MPI_Test(MPI_Request *request,
int *flag,
MPI_Status * status)该函数是非阻塞的;
它根据输入参数request,返回异步收发操作的状态;
其输出参变量flag有以下几种取值:
0:代表异步收发操作还未完成;
非0:代表异步收发操作已经完成,且status包含了该消息的信息(status.MPI_TAG、status.MPI_SOURCE)MPI_Wait(MPI_Request *request,
MPI_Status * status)该函数阻塞等待请求request(与某异步收发操作关联)完成;返回值status同上。
-
-
同步(阻塞)方法求解数组中最值程序
-
代码示例:
#include "mpi.h" #include <stdio.h> #include <stdlib.h> #define MAX 10 int num_procs; double x[MAX] = {1,3,56,24,54,54,35,245,23,52}; // Input array int main(int argc, char *argv[]) { int i,start, stop; int myid; double my_min, others_min; // Minimum double my_max, others_max; // Maximum MPI_Status st; MPI_Init(&argc,&argv); // Initialize MPI_Comm_size(MPI_COMM_WORLD, &num_procs); // Get # processors MPI_Comm_rank(MPI_COMM_WORLD, &myid); // Get my rank (id) /* -------------------------------------- * Find the min. among my numbers * -------------------------------------- */ int n = MAX/num_procs; start = myid * n; //某并发进程子任务的数组起始位置 if ( myid != (num_procs-1) ) //最后一个进程不一定能分到n个元素 { stop = start + n; } else { stop = MAX; } my_min = x[start]; //假定起始位置为最小值 for (i = start+1; i < stop; i = i + 1 ) { if ( x[i] < my_min ) my_min = x[i]; } if ( myid == 0 ) { /* ------------------------------------- Get the min from others and compare ------------------------------------- */ for (i = 1; i < num_procs; i++) { MPI_Recv(&others_min, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &st); if ( others_min < my_min ) my_min = others_min; } printf("The min number:%f\n",my_min); } else { MPI_Send(&my_min, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); } /* -------------------------------------- Now find the max. among my numbers -------------------------------------- */ my_max = x[start]; for (i = start+1; i < stop; i = i + 1 ) { if ( x[i] > my_max ) my_max = x[i]; } if ( myid == 0 ) { /* ------------------------------------- Get the max from others and compare ------------------------------------- */ for (i = 1; i < num_procs; i++) { MPI_Recv(&others_max, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &st); if ( others_max > my_max ) my_max = others_max; } printf("The max number:%f\n",my_max); } else { MPI_Send(&my_max, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); } MPI_Finalize(); }
-
本代码中,因为收发函数是阻塞(同步)的,所以进程计算完之后执行收发操作时,必须阻塞着等待收发完成才能进行下一步操作,这无疑浪费了宝贵的时间。最好的方法是在程序的最后才进行同步处理。而这就不得不依靠下文叙述的异步处理机制了。
-
但使用异步通信时必须要确保每一次异步收发,都必须使用不同的缓存区,以防止收发操作还未完成时缓存区被污染。
-
-
异步(非阻塞)方法求解数组中最值程序
-
代码示例:
#include "mpi.h" #include <stdio.h> #include <stdlib.h> #define MAX 10 int num_procs; double x[MAX] = {1,3,56,24,54,54,35,245,23,52}; // Input array int main(int argc, char *argv[]) { int i,start, stop; int myid; double my_min; // Minimum double others_min[100]; // Save minimum separately double my_max; // Maximum double others_max[100]; // Save maximum separately MPI_Status st; MPI_Request rq_min[100], rq_max[100]; // Status variables MPI_Init(&argc,&argv); // Initialize MPI_Comm_size(MPI_COMM_WORLD, &num_procs); // Get # processors MPI_Comm_rank(MPI_COMM_WORLD, &myid); // Get my rank (id) /* -------------------------------------- Find the min. among my numbers -------------------------------------- */ int n = MAX/num_procs; start = myid * n; //某并发进程子任务的数组起始位置 if ( myid != (num_procs-1) ) //最后一个进程不一定能分到n个元素 { stop = start + n; } else { stop = MAX; } my_min = x[start]; //假定起始位置为最小值 for (i = start+1; i < stop; i = i + 1 ) { if ( x[i] < my_min ) my_min = x[i]; } if ( myid == 0 ) { /* ------------------------------------- Get the min from others and compare ------------------------------------- */ for (i = 1; i < num_procs; i++) { MPI_Irecv(&others_min[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &rq_min[i]); } } else { MPI_Isend(&my_min, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD,&rq_min[0]); } /* -------------------------------------- Now find the max. among my numbers -------------------------------------- */ my_max = x[start]; for (i = start+1; i < stop; i = i + 1 ) { if ( x[i] > my_max ) my_max = x[i]; } if ( myid == 0 ) { /* ------------------------------------- Get the max from others and compare ------------------------------------- */ for (i = 1; i < num_procs; i++) { MPI_Irecv(&others_max[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &rq_max[i]); } } else { MPI_Isend(&my_max, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD,&rq_max[0]); } /* -------------------------------------- Now synchronize to compute results -------------------------------------- */ if ( myid == 0 ) { for ( i = 1; i < num_procs; i++) { MPI_Wait( &rq_min[i], &st ); if ( others_min[i] < my_min ) my_min = others_min[i]; } for ( i = 1; i < num_procs; i++) { MPI_Wait( &rq_max[i], &st ); if ( others_max[i] > my_max ) my_max = others_max[i]; } printf("min = %f\n",my_min); printf("max = %f\n",my_max); } else { // The other processes must wait until their messages // has been received before exiting !!! MPI_Wait( &rq_min[0], &st ); MPI_Wait( &rq_max[0], &st ); } MPI_Finalize(); }
-