【MPI】高级集体通信
MPI_Reduce
和 MPI_Allreduce
都是用于在 MPI (Message Passing Interface) 编程模型中进行数据汇总的函数,通常用于并行计算中的通信操作。它们的区别在于是否包括所有进程的结果。这两个API和MPI_Gather
和 MPI_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_Allreduce
与 MPI_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;
}
参考:这里