【MPI】阻塞点对点通信

【MPI】阻塞点对点通信

Blocking Point-to-Point Communication in MPI

本文将介绍 MPI 中的阻塞式点对点通信,包括基本数据类型、MPI_SendMPI_Recv 函数、MPI_Status 结构体,以及 MPI_Get_countMPI_Probe 函数。

MPI 基础数据类型

在 MPI 中,数据类型(datatype)是数据结构的重要部分,用于定义消息传递中数据的格式。MPI 提供了一些基础数据类型,简洁明了对应于 C 语言中的基本数据类型:

MPI datatypeC equivalent
MPI_SHORTshort int
MPI_INTint
MPI_LONGlong int
MPI_LONG_LONGlong long int
MPI_UNSIGNED_CHARunsigned char
MPI_UNSIGNED_SHORTunsigned short int
MPI_UNSIGNEDunsigned int
MPI_UNSIGNED_LONGunsigned long int
MPI_UNSIGNED_LONG_LONGunsigned long long int
MPI_FLOATfloat
MPI_DOUBLEdouble
MPI_LONG_DOUBLElong double
MPI_BYTEchar

此外,MPI 还允许构造复杂的数据类型,以便传递自定义的复合结构。

MPI_Send 和 MPI_Recv

在 MPI 中,阻塞式的点对点通信函数主要是 MPI_SendMPI_Recv。它们分别用于在进程之间发送和接收消息。所谓“阻塞”是指函数会在数据发送或接收完成后才返回,确保通信的完整性。

MPI_Send

MPI_Send 是发送消息的阻塞式函数,将数据从一个进程发送到另一个进程。MPI_Send 的发送操作会在数据被成功复制到系统缓存或目标进程接收到数据之前阻塞发送进程,从而确保数据一致性。 其函数原型如下:

int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);
  • buf:指向待发送数据的指针。
  • count:待发送的数据个数。
  • datatype:发送数据的数据类型(如 MPI_INT)。
  • dest:目标进程的 rank(编号)。
  • tag:消息标签,用于区分不同类型的消息。
  • comm:通信域(如 MPI_COMM_WORLD)。

例如,进程 0 可以使用以下代码将一个整型数组发送给进程 1:

int numbers[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
MPI_Send(numbers, 10, MPI_INT, 1, 0, MPI_COMM_WORLD);
MPI_Recv

MPI_Recv 是接收消息的阻塞式函数,它在消息被完全接收到本地缓冲区后才返回。其函数原型如下:

int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status);
  • buf:指向接收缓冲区的指针。
  • count:预期接收的数据个数。
  • datatype:接收数据的数据类型(如 MPI_INT)。
  • source:消息源进程的 rank。如果设置为 MPI_ANY_SOURCE,则可以接收来自任何进程的消息。
  • tag:消息标签。如果设置为 MPI_ANY_TAG,则可以接收任何标签的消息。
  • comm:通信域(如 MPI_COMM_WORLD)。
  • status:指向 MPI_Status 结构体的指针,用于存储消息的相关信息。

例如,进程 1 可以使用以下代码接收进程 0 发送的整型数组:

int numbers[10];
MPI_Status status;
MPI_Recv(numbers, 10, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);

MPI_Status 结构体

MPI_Status 结构体用于存储消息传递完成后的相关信息,例如消息的来源、标签和接收到的数据量。MPI_Recv 会通过 status 参数返回消息的信息。MPI_Status 结构体的常用字段如下:

  • MPI_SOURCE:消息的来源进程的 rank。
  • MPI_TAG:消息的标签。
  • MPI_ERROR:错误代码。

例如,可以通过 status 获取消息的来源和标签:

printf("Message source = %d, tag = %d\n", status.MPI_SOURCE, status.MPI_TAG);

MPI_Get_count

MPI_Get_count 函数用于获取接收消息的实际数据量。由于 MPI_Recv 函数需要提供一个缓冲区和数据量的上限(count),但实际接收到的数据量可能少于 count,因此可以使用 MPI_Get_count 来获取接收的实际数据量。

其函数原型如下:

int MPI_Get_count(const MPI_Status *status, MPI_Datatype datatype, int *count);
  • statusMPI_Status 结构体,通常是 MPI_Recv 返回的 status
  • datatype:数据类型。
  • count:指向整数的指针,用于存储实际接收的数据量。

例如:

int actual_count;
MPI_Get_count(&status, MPI_INT, &actual_count);
printf("Received %d integers\n", actual_count);

MPI_Probe

MPI_Probe 是一种非阻塞的消息探测函数,允许进程在不接收消息的情况下检查是否有特定消息到达。这在动态内存分配等场景中非常有用,因为我们可以在接收数据前确定消息的长度。

其函数原型如下:

int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status);
  • source:消息来源进程的 rank。如果设置为 MPI_ANY_SOURCE,则可以探测来自任何进程的消息。
  • tag:消息标签。如果设置为 MPI_ANY_TAG,则可以探测任何标签的消息。
  • comm:通信域。
  • status:指向 MPI_Status 结构体的指针,用于存储消息的相关信息。

例如,以下代码段使用 MPI_Probe 来检查消息长度:

MPI_Status status;
MPI_Probe(0, 0, MPI_COMM_WORLD, &status);
int count;
MPI_Get_count(&status, MPI_INT, &count);
int *buffer = (int *)malloc(count * sizeof(int));
MPI_Recv(buffer, count, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);

结论

MPI 的阻塞式点对点通信在并行编程中具有重要意义。通过使用 MPI_SendMPI_Recv 函数,进程可以在不同的节点之间传递数据,确保消息的可靠传输。同时,利用 MPI_StatusMPI_Get_countMPI_Probe 等辅助函数,可以更灵活地控制消息传递过程。

实例
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>

//int main() { 
//  const int PING_PONG_LIMIT = 10;
//  MPI_Init(NULL, NULL);
//  int rank, size;
//  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
//  MPI_Comm_size(MPI_COMM_WORLD, &size);
//
//
//  int ping_pong_count = 0;
//  int partner_rank = (rank + 1) % 2;
//  while (ping_pong_count < PING_PONG_LIMIT) {
//    if (rank == ping_pong_count % 2) {
//      ping_pong_count++;
//      // 注意这个ping_pong_count被作为缓存,所以看起来两个进程共享ping_pong_count变量,但实际上两个进程是独立的
//      MPI_Send(&ping_pong_count, 1, MPI_INT, partner_rank, 0, MPI_COMM_WORLD);
//      printf("rank %d: sent and incremented count %d to rank %d\n", rank, ping_pong_count, partner_rank);
//    } else {
//      MPI_Recv(&ping_pong_count, 1, MPI_INT, partner_rank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
//      printf("rank %d: received count %d from rank %d\n", rank, ping_pong_count, partner_rank);
//    }
//  }
//
//  MPI_Finalize();
//}


//int main() {
//  MPI_Init(NULL, NULL);
//  int rank, size;
//  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
//  MPI_Comm_size(MPI_COMM_WORLD, &size);
//  
//  int token;
//  if (rank) {
//    MPI_Recv(&token, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD,
//             MPI_STATUS_IGNORE);
//    printf("Process %d received token %d from process %d\n", rank, token,
//           rank - 1);
//  } else {
//    token = -1;
//  }
//
//  MPI_Send(&token, 1, MPI_INT, (rank + 1) % size, 0, MPI_COMM_WORLD);
//  if (rank == 0) {
//    MPI_Recv(&token, 1, MPI_INT, size - 1, 0, MPI_COMM_WORLD,
//             MPI_STATUS_IGNORE);
//    printf("Process %d received token %d from process %d\n", rank, token,
//           size - 1);
//  }
//
//  MPI_Finalize();
//}



#include <time.h>

//int main() {
//  MPI_Init(NULL, NULL);
//  int rank, size;
//  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
//  MPI_Comm_size(MPI_COMM_WORLD, &size);
//
//  if (size != 2) {
//    fprintf(stderr, "Must use two processes for this exapmle\n");
//    // 在并行计算进程中终止程序,1是erroecode
//    MPI_Abort(MPI_COMM_WORLD, 1);
//  }
//
//  const int MAX_NUMBER = 100;
//  int numbers[MAX_NUMBER];
//  int num_cnt;
//  if (rank==0) {
//    srand(time(NULL));
//    // rand()/(float)RAND_MAX生成0~1之间的随机数
//    num_cnt=(rand()/(float)RAND_MAX)*MAX_NUMBER;
//    MPI_Send(numbers,num_cnt, MPI_INT, 1, 666, MPI_COMM_WORLD);
//    printf("rank %d: sent %d numbers to rank %d\n", rank, num_cnt, 1);
//  } else if (rank == 1) {
//    MPI_Status status;
//    MPI_Recv(numbers, MAX_NUMBER, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD,&status);
//    MPI_Get_count(&status, MPI_INT, &num_cnt);
//    printf("1 received %d numbers from 0. Message source = %d,tag = %d\n",
//           num_cnt, status.MPI_SOURCE, status.MPI_TAG);
//  }
//
//  MPI_Finalize();
//  return 0;
//}


 int main() {
   MPI_Init(NULL, NULL);
   int rank, size;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   MPI_Comm_size(MPI_COMM_WORLD, &size);

   if (size != 2) {
     fprintf(stderr, "Must use two processes for this exapmle\n");
     // 在并行计算进程中终止程序,1是erroecode
     MPI_Abort(MPI_COMM_WORLD, 1);
   }

   
   int num_cnt;
   if (rank==0) {
     const int MAX_NUMBER = 100;
     int numbers[MAX_NUMBER];
     srand(time(NULL));
     // rand()/(float)RAND_MAX生成0~1之间的随机数
     num_cnt=(rand()/(float)RAND_MAX)*MAX_NUMBER;
     MPI_Send(numbers,num_cnt, MPI_INT, 1, 666, MPI_COMM_WORLD);
     printf("rank %d: sent %d numbers to rank %d\n", rank, num_cnt, 1);
   } else if (rank == 1) {
     MPI_Status status;
     MPI_Probe(0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
     MPI_Get_count(&status, MPI_INT, &num_cnt);
     int* numbers = (int*)malloc(sizeof(int) * num_cnt);

     MPI_Recv(numbers, num_cnt, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD,
              &status);
     printf("1 dynamically received %d numbers from 0. Message source = %d,tag = %d\n",
            num_cnt, status.MPI_SOURCE, status.MPI_TAG);
   }

   MPI_Finalize();
   return 0;
 }

参考:传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值