【MPI】高级集体通信

【MPI】高级集体通信

MPI_ReduceMPI_Allreduce 都是用于在 MPI (Message Passing Interface) 编程模型中进行数据汇总的函数,通常用于并行计算中的通信操作。它们的区别在于是否包括所有进程的结果。这两个API和MPI_GatherMPI_Allgather 类似都是汇总函数,不过前者只获得汇集数据的操作后的结果,而后者只是简单汇集数据。

1. MPI_Reduce

MPI_Reduce 用于将多个进程的数据汇总到一个特定的进程。这个汇总操作通常是对数据进行某种计算(例如求和、求最大值等)。

语法:
int MPI_Reduce(
    const void *sendbuf,     // 发送缓冲区,包含要被减少的数据
    void *recvbuf,           // 接收缓冲区,接收汇总结果
    int count,               // 发送/接收数据的元素数量
    MPI_Datatype datatype,   // 数据类型
    MPI_Op op,               // 操作类型,如求和、求最大值等
    int root,                // 汇总操作的目标进程编号
    MPI_Comm comm            // 通信子
);
  • sendbuf: 每个进程的输入数据。
  • recvbuf: 汇总后的结果仅会被 root 进程接收。
  • count: 每个进程发送数据的元素个数。
  • datatype: 数据类型(例如 MPI_INT, MPI_DOUBLE 等)。
  • op: 汇总操作(例如 MPI_SUM, MPI_MAX, MPI_MIN 等)。
  • root: 目标进程,数据将会被汇总到该进程。
  • comm: 通信子,定义进程之间的通信范围。
示例:

假设我们有 4 个进程,每个进程有一个整数数据,目标是计算所有进程数据的总和。

int data = rank + 1;  // 假设进程 rank 的数据是 rank + 1
int result;
MPI_Reduce(&data, &result, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
if (rank == 0) {
    printf("Total sum: %d\n", result);
}

在这个例子中,所有进程的数据将会汇总到进程 0,进程 0 会输出结果。

2. MPI_Allreduce

MPI_AllreduceMPI_Reduce 类似,不同之处在于它将汇总操作的结果发送到 所有进程,而不是仅仅发送给 root 进程。所有进程都将拥有汇总结果的副本。

语法:
int MPI_Allreduce(
    const void *sendbuf,     // 发送缓冲区,包含要被减少的数据
    void *recvbuf,           // 接收缓冲区,接收汇总结果
    int count,               // 发送/接收数据的元素数量
    MPI_Datatype datatype,   // 数据类型
    MPI_Op op,               // 操作类型,如求和、求最大值等
    MPI_Comm comm            // 通信子
);
  • sendbuf: 每个进程的输入数据。
  • recvbuf: 所有进程将接收到的汇总结果。
  • count: 每个进程发送数据的元素个数。
  • datatype: 数据类型(例如 MPI_INT, MPI_DOUBLE 等)。
  • op: 汇总操作(例如 MPI_SUM, MPI_MAX, MPI_MIN 等)。
  • comm: 通信子,定义进程之间的通信范围。
示例:

假设我们有 4 个进程,每个进程有一个整数数据,目标是计算所有进程数据的总和,且所有进程都需要得到结果。

int data = rank + 1;  // 假设进程 rank 的数据是 rank + 1
int result;
MPI_Allreduce(&data, &result, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
printf("Total sum in process %d: %d\n", rank, result);

在这个例子中,所有进程的 result 都会被设置为汇总后的总和,并且每个进程都会打印出相同的结果。

总结:

  • MPI_Reduce: 将多个进程的数据汇总到一个特定的进程(通常是 root 进程)。
  • MPI_Allreduce: 将多个进程的数据汇总,并将结果发送到所有进程。

MPI_Reduce 适用于只关心一个进程获得汇总结果的场景,而 MPI_Allreduce 适用于需要所有进程获取汇总结果的情况。

示例

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cassert>
#include <ctime>
#include <mpi.h>
#include <iostream>


// 计算分布式数据的平均值-----------------------------

float* CreateRandNums(int n) {
  float* rand_nums = (float*)malloc(n * sizeof(float));
  assert(rand_nums);
  for (int i = 0; i < n; ++i) {
    rand_nums[i] = (float)rand() / RAND_MAX;
  }
  return rand_nums;
}

//int main(int argc, char** argv) {
//  if (argc != 2) {
//    std::cerr << "Usage: avg num_elements_per_proc" << std::endl;
//    exit(1);
//  }
//  int num_elements_per_proc = atoi(argv[1]);
//
//  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_nums=CreateRandNums(num_elements_per_proc);
//
//  float local_sum = 0;
//  for (int i = 0; i < num_elements_per_proc; ++i) {
//    local_sum += rand_nums[i];
//  }
//  std::cout << "local_sum for process " << rank << " - " << local_sum
//            << " ,avg = "<<local_sum / num_elements_per_proc << std::endl;
//
//  float global_sum;
//  MPI_Reduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM, 0, MPI_COMM_WORLD);
//  if (rank == 0) {
//    std::cout << "global_sum = " << global_sum
//              << " avg = " << global_sum / (world_size * num_elements_per_proc)
//              << std::endl;
//  }
//  free(rand_nums);
//  MPI_Barrier(MPI_COMM_WORLD);
//  MPI_Finalize();
//
//  return 0;
//}



// 计算分布式数据的方差-----------------------------

 int main(int argc, char** argv) {
   if (argc != 2) {
     std::cerr << "Usage: avg num_elements_per_proc" << std::endl;
     exit(1);
   }
   int num_elements_per_proc = atoi(argv[1]);

   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_nums=CreateRandNums(num_elements_per_proc);

   float local_sum = 0;
   for (int i = 0; i < num_elements_per_proc; ++i) {
     local_sum += rand_nums[i];
   }
   std::cout << "local_sum for process " << rank << " - " << local_sum
             << " ,avg = "<<local_sum / num_elements_per_proc << std::endl;

   float global_sum;
   MPI_Allreduce(&local_sum, &global_sum, 1, MPI_FLOAT, MPI_SUM,
                 MPI_COMM_WORLD);
   float mean = global_sum / (num_elements_per_proc * world_size);
   float local_sq_diff = 0;
   for (int i = 0; i < num_elements_per_proc; i++) {
     local_sq_diff += (rand_nums[i] - mean) * (rand_nums[i] - mean);
   }
   
   float global_sq_diff;
   MPI_Reduce(&local_sq_diff, &global_sq_diff, 1, MPI_FLOAT, MPI_SUM,
              0, MPI_COMM_WORLD);

   if (rank == 0) {
     float stddev = sqrt(global_sq_diff / (num_elements_per_proc * world_size));
     std::cout << "mean = "<<mean << " stddev = " << stddev << std::endl;
   }

   free(rand_nums);
   MPI_Barrier(MPI_COMM_WORLD);
   MPI_Finalize();

   return 0;
 }

参考:这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值