【MPI】计算并行排名
接下来通过以下这个程序来综合复习基本集体通信。
问题背景
并行排名问题:当每一个进程都在其本地内存中存储了一个数,所有进程中存储的数字构成了一个数字集合(set of numbers),了解该数相对于整个数字集合的顺序是有用的。例如,用户可能正在对MPI群集中的处理器进行基准测试,并想知道每个处理器相对于其他处理器有多快。这个信息可用于安排、调度任务等。可以想象,如果所有其他数字分散在各个进程中,那么很难找出一个数字相对于所有其他数字的顺序。这个并行排名问题是我们在本课中要解决的问题。
程序
程序实现了一个并行排序(或称为并行排名)的功能。该功能使每个进程在拥有自己的数据(例如一个随机生成的数字)后,能够得到该数据在整个进程集合中的排序排名。以下是程序的基本功能与步骤:
- 每个进程存储一个数:程序初始化了多个进程,每个进程独立生成一个随机数用来模拟实际的数据。这些数字构成了一个“全体进程的数字集合”。
- 根进程收集数据:程序通过
MPI_Gather函数将所有进程的数值收集到根进程(通常是rank 0),使得根进程拥有全体进程的数据集合。 - 根进程排序:根进程将这些收集到的数据进行排序,确定每个数值在整个集合中的排名顺序。
- 广播排名结果:根进程将每个进程对应数值的排名结果发送给每个进程(即通过
MPI_Scatter将排名结果分发回各个进程)。每个进程最终得到了自己的数值在整个集合中的排名。 - 输出结果:每个进程输出其进程编号 (
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;
}
参考:这里


被折叠的 条评论
为什么被折叠?



