我们的函数需要在每个进程中取一个数字,并返回其所有流程中所有其他数字的相关排名。
与此同时,我们将需要其他杂项信息:正在使用的通信器以及正在排名的数字的数据类型。
TMPI_Rank(
void *send_data,
void *recv_data,
MPI_Datatype datatype,
MPI_Comm comm)
TMPI_Rank接收一个包含一个datatype类型的send_data缓冲区。
recv_data在包含send_data的rank值的每个进程上只收到一个整数。
comm变量是正在进行排名的通信器。
解决并行排序问题的第一步是排序所有进程的所有数字。 这必须完成,以便我们可以在整个数字集中找到每个数字的排名。
最简单的方法是将所有数字收集到一个进程并对数字进行排序。
void *gather_numbers_to_root(void *number, MPI_Datatype datatype,
MPI_Comm comm) {
int comm_rank, comm_size;
MPI_Comm_rank(comm, &comm_rank);
MPI_Comm_size(comm, &comm_size);
// 根据使用的数据类型,给根进程分配size
int datatype_size;
MPI_Type_size(datatype, &datatype_size);
void *gathered_numbers;
if (comm_rank == 0) {
//malloc一个datatype_size * comm_size长度的数组
gathered_numbers = malloc(datatype_size * comm_size);
}
// 收集根进程的所有数字
MPI_Gather(number, 1, datatype, gathered_numbers, 1,
datatype, 0, comm);
return gathered_numbers;
}
//接下来进行排序,排序前需要先定义结构体
typedef struct {
int comm_rank;
union {
float f;
int i;
} number;
} CommRankNumber;
int *get_ranks(void *gathered_numbers, int gathered_number_count,
MPI_Datatype datatype) {
int datatype_size;
MPI_Type_size(datatype, &datatype_size);
// 将收集的数字数组转换为CommRankNumbers数组。
// 这使我们能够对数字进行排序,?并保留拥有数字的进程信息?
CommRankNumber *comm_rank_numbers = malloc(
gathered_number_count * sizeof(CommRankNumber));
int i;
for (i = 0; i < gathered_number_count; i++) {
//rank信息
comm_rank_numbers[i].comm_rank = i;
//内存拷贝
//gathered_numbers是指针地址 每次从初始位置后移 (i * datatype_size)开始取 取 datatype_size个
memcpy(&(comm_rank_numbers[i].number),
gathered_numbers + (i * datatype_size),
datatype_size);
}
// 根据数据类型进行排序
if (datatype == MPI_FLOAT) {
qsort(comm_rank_numbers, gathered_number_count,
sizeof(CommRankNumber), &compare_float_comm_rank_number);
} else {
qsort(comm_rank_numbers, gathered_number_count,
sizeof(CommRankNumber), &compare_int_comm_rank_number);
}
// comm_rank_numbers被排序,为每个进程创建一个编号数组。 该数组的第i个元素包含进程i发送的数字的编号。
//数字排序后,我们必须以正确的顺序创建一个排列数组,以便它们可以scatter回请求进程。
int *ranks = (int *)malloc(sizeof(int) * gathered_number_count);
for (i = 0; i < gathered_number_count; i++) {
ranks[comm_rank_numbers[i].comm_rank] = i;
}
// Clean up and return the rank array
free(comm_rank_numbers);
return ranks;
}
int TMPI_Rank(void *send_data, void *recv_data, MPI_Datatype datatype,
MPI_Comm comm) {
// 首先检查基本情况 - 仅支持此函数的MPI_INT和MPI_FLOAT。
if (datatype != MPI_INT && datatype != MPI_FLOAT) {
return MPI_ERR_TYPE;
}
int comm_size, comm_rank;
MPI_Comm_size(comm, &comm_size);
MPI_Comm_rank(comm, &comm_rank);
// 要计算编号,我们必须将数字收集到一个进程中,对数字进行排序,然后分散结果的等级值。
//首先收集comm的进程0的数字。
void *gathered_numbers = gather_numbers_to_root(send_data, datatype,
comm);
// 获得每个进程的编号
int *ranks = NULL;
if (comm_rank == 0) {
ranks = get_ranks(gathered_numbers, comm_size, datatype);
}
// Scatter the rank results
MPI_Scatter(ranks, 1, MPI_INT, recv_data, 1, MPI_INT, 0, comm);
// Do clean up
if (comm_rank == 0) {
free(gathered_numbers);
free(ranks);
}
}