基于消息传递的并发编程(MPI)之异步收发

4.2 MPI的异步通信(非阻塞收发)

  • 同步通信:当一个消息发送操作完全完成(消息接收者已经收到了该消息)时,称为同步发送;同理,当一个消息接收操作完全完成(消息接收者已经收到了该消息)时,称为同步接收。上文中提到的MPI_SendMPI_Recv就是同步收发函数,或者说是阻塞的收发函数

  • 异步通信:当一个消息发送操作部分完成(不管消息接收者是否已经收到消息,我就是立即返回)时,称为异步发送;同理,当一个消息接收操作部分完成(不管是否接收到了消息,我就是立即返回)时,称为异步接收。下文中即将提到的MPI_IsendMPI_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();
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leon_George

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值