【MPI】计算并行排名

【MPI】计算并行排名

接下来通过以下这个程序来综合复习基本集体通信。

问题背景

并行排名问题:当每一个进程都在其本地内存中存储了一个数,所有进程中存储的数字构成了一个数字集合(set of numbers),了解该数相对于整个数字集合的顺序是有用的。例如,用户可能正在对MPI群集中的处理器进行基准测试,并想知道每个处理器相对于其他处理器有多快。这个信息可用于安排、调度任务等。可以想象,如果所有其他数字分散在各个进程中,那么很难找出一个数字相对于所有其他数字的顺序。这个并行排名问题是我们在本课中要解决的问题。

程序

程序实现了一个并行排序(或称为并行排名)的功能。该功能使每个进程在拥有自己的数据(例如一个随机生成的数字)后,能够得到该数据在整个进程集合中的排序排名。以下是程序的基本功能与步骤:

  1. 每个进程存储一个数:程序初始化了多个进程,每个进程独立生成一个随机数用来模拟实际的数据。这些数字构成了一个“全体进程的数字集合”。
  2. 根进程收集数据:程序通过 MPI_Gather 函数将所有进程的数值收集到根进程(通常是 rank 0),使得根进程拥有全体进程的数据集合。
  3. 根进程排序:根进程将这些收集到的数据进行排序,确定每个数值在整个集合中的排名顺序。
  4. 广播排名结果:根进程将每个进程对应数值的排名结果发送给每个进程(即通过 MPI_Scatter 将排名结果分发回各个进程)。每个进程最终得到了自己的数值在整个集合中的排名。
  5. 输出结果:每个进程输出其进程编号 (rank)、生成的随机数,以及这个数值在全体进程中的排名结果。
#include <cstdlib>
#include <mpi.h>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>

struct CommRankNumber {
  int rank;
  // number保存进程了要排序的数字
  union {
    float f;
    int i;
  }number;
};

void* GatherNumbersToRoot(void* number, MPI_Datatype datatype, MPI_Comm comm) {
  int world_size, rank;
  MPI_Comm_size(comm, &world_size);
  MPI_Comm_rank(comm, &rank);

  int datatype_size;
  // 获取datatype的大小,使用MPI_Type_size更具一般性
  MPI_Type_size(datatype, &datatype_size);
  void* gethered_numbers=nullptr;
  if (rank == 0) {
    gethered_numbers = malloc(world_size * datatype_size);
  }
  MPI_Gather(number, 1, datatype, gethered_numbers, 1, datatype, 0, comm);
  // 除根进程外,其他进程的gathered_numbers为nullptr
  return gethered_numbers;
}

int* GetRanks(void* gathered_numbers, int gathered_numner_cnt,
              MPI_Datatype datatype) {
  int datatype_size;
  MPI_Type_size(datatype, &datatype_size);

  CommRankNumber* comm_rank_numbers =
      (CommRankNumber*)malloc(gathered_numner_cnt * sizeof(CommRankNumber));
  for (int i = 0; i < gathered_numner_cnt; ++i) {
    comm_rank_numbers[i].rank = i;
    memcpy(&(comm_rank_numbers[i].number),
           (char*)gathered_numbers + (i * datatype_size), datatype_size);
  }
  if (datatype == MPI_FLOAT) {
    std::sort(comm_rank_numbers, comm_rank_numbers + gathered_numner_cnt,
              [](CommRankNumber a, CommRankNumber b) {
                return a.number.f < b.number.f;
              });
  } else {
    std::sort(comm_rank_numbers, comm_rank_numbers + gathered_numner_cnt,
              [](CommRankNumber a, CommRankNumber b) {
                return a.number.i < b.number.i;
              });
  }

  int* ranks = (int*)malloc(sizeof(int) * gathered_numner_cnt);
  for (int i = 0; i < gathered_numner_cnt; ++i) {
    ranks[comm_rank_numbers[i].rank] = i;
  }
  free(comm_rank_numbers);
  return ranks;
}

int MyMPIRank(void* send_data, void* recv_data, MPI_Datatype datatype,
              MPI_Comm comm) {
  if (datatype != MPI_INT && datatype != MPI_FLOAT) {
    return MPI_ERR_TYPE;
  }

  int world_size, rank;
  MPI_Comm_size(comm, &world_size);
  MPI_Comm_rank(comm, &rank);

  // 收集所有进程的数字到根进程,除根进程外,其他进程的gathered_numbers为nullptr
  void* gathered_numbers = GatherNumbersToRoot(send_data, datatype, comm);

  int* ranks = nullptr;
  if (rank == 0) {
    // 根进程对所有数字进行排序,并返回排序后的rank及对应的数字先后关系
    ranks = GetRanks(gathered_numbers, world_size, datatype);
  }

  MPI_Scatter(ranks, 1, MPI_INT, recv_data, 1, MPI_INT, 0, comm);
  if (rank == 0) {
    free(ranks);
    free(gathered_numbers);
  }
  return MPI_SUCCESS;
}


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

  srand(time(NULL) * rank);
  // 各自生成随机数模拟数据
  float rand_num = rand() / (float)RAND_MAX;
  int result;
  MyMPIRank(&rand_num, &result, MPI_FLOAT, MPI_COMM_WORLD);
  std::cout<<"rank:"<<rank<<"rand_num:"<<rand_num<<" result:"<<result<<std::endl;

  MPI_Barrier(MPI_COMM_WORLD);
  MPI_Finalize();
  return 0;
}

参考:这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值