参考链接:
(1)https://www.cnblogs.com/cuancuancuanhao/p/8438608.html
(2)MPI之聚合通信-Scatter,Gather,Allgather
https://blog.csdn.net/sinat_22336563/article/details/70229243
#MPI 集合通信函数
MPI_Reduce()
MPI_Allreduce()
MPI_Bcast()
MPI_Scatter()
MPI_Gather()
MPI_Allgather()
MPI_Scan()
MPI_Reduce_Scatter()
MPI_Reduce
#规约函数 MPI_Reduce(),将通信子内各进程的同一个变量参与规约计算,并向指定的进程输出计算结果
#函数原型
MPI_METHOD MPI_Reduce(
_In_range_(!= , recvbuf) _In_opt_ const void* sendbuf, // 指向输入数据的指针
_When_(root != MPI_PROC_NULL, _Out_opt_) void* recvbuf, // 指向输出数据的指针,即计算结果存放的地方
_In_range_(>= , 0) int count, // 数据尺寸,可以进行多个标量或多个向量的规约
_In_ MPI_Datatype datatype, // 数据类型
_In_ MPI_Op op, // 规约操作类型
_mpi_coll_rank_(root) int root, // 目标进程号,存放计算结果的进程
_In_ MPI_Comm comm // 通信子
);
#使用范例
int main()
{
int size, rank, data, dataCollect;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
data = rank;// 参与计算的数据
MPI_Reduce((void *)&data, (void *)&dataCollect, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);// 所有的进程都要调用,而不是只在目标进程中调用
MPI_Finalize();
}
MPI_Allreduce
#规约并广播函数 MPI_Allreduce(),在计算规约的基础上,将计算结果分发到每一个进程中,相比于 MPI_Reduce(),只是少了一个 root 参数。除了简单的先规约再广播的方法,书中介绍了蝶形结构全局求和的方法
#函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Allreduce(
_In_range_(!= , recvbuf) _In_opt_ const void* sendbuf,
_Out_opt_ void* recvbuf,
_In_range_(>= , 0) int count,
_In_ MPI_Datatype datatype,
_In_ MPI_Op op,
_In_ MPI_Comm comm
);
#使用范例
int main()
{
int size, rank, data, dataCollect;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
data = rank;
MPI_Reduce((void *)&data, (void *)&dataCollect, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);// 所有的进程都要调用
MPI_Finalize();
}
MPI_Bcast
#广播函数 MPI_Bcast(),将某个进程的某个变量的值广播到该通信子中所有进程的同名变量中
#函数原型
MPI_METHOD MPI_Bcast(
_Pre_opt_valid_ void* buffer, // 指向输入 / 输出数据的指针
_In_range_(>= , 0) int count, // 数据尺寸
_In_ MPI_Datatype datatype, // 数据类型
_mpi_coll_rank_(root) int root, // 广播源进程号
_In_ MPI_Comm comm // 通信子
);
#使用范例
int main
{
int size, rank, data;
MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
data = rank;
MPI_Bcast((void *)&data, 1, MPI_INT, 0, MPI_COMM_WORLD);// 所有的进程都要调用,调用后所有 data 均被广播源进程的值覆盖
MPI_Finalize();
}
MPI_Scatter
#散射函数 MPI_Scatter(),将向量数据分段发送到各进程中
#函数原型
_Pre_satisfies_(sendbuf != MPI_IN_PLACE) MPI_METHOD MPI_Scatter(
_In_range_(!= , recvbuf) _In_opt_ const void* sendbuf, // 指向需要分发的数据的指针
_In_range_(>= , 0) int sendcount, // 分发到每一个进程的数据量,注意不是分发的数据总量
_In_ MPI_Datatype sendtype, // 分发数据类型
_When_(root != MPI_PROC_NULL, _Out_opt_) void* recvbuf, // 指向接收的数据的指针
_In_range_(>= , 0) int recvcount, // 接受数据量,不小于上面分发到每一个进程的数据量
_In_ MPI_Datatype recvtype, // 接收数据类型
_mpi_coll_rank_(root) int root, // 分发数据源进程号
_In_ MPI_Comm comm // 通信子
);
// 宏定义,mpi.h
#define MPI_IN_PLACE ((void*)(MPI_Aint)-1 // MPI_Aint 为 __int64 类型,表示地址
MPI_Gather
#聚集函数 MPI_Gather(),将各进程中的向量数据分段聚集到一个进程的大向量中
#函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Gather(
_In_opt_ _When_(sendtype == recvtype, _In_range_(!= , recvbuf)) const void* sendbuf,// 指向需要聚集的数据的指针
_In_range_(>= , 0) int sendcount, // 每个进程中进行聚集的数据量,不是聚集的数据总量
_In_ MPI_Datatype sendtype, // 发送数据类型
_When_(root != MPI_PROC_NULL, _Out_opt_) void* recvbuf, // 指向接收数据的指针
_In_range_(>= , 0) int recvcount, // 从每个进程接收的接收数据量,不是聚集的数据总量
_In_ MPI_Datatype recvtype, // 接收数据类型
_mpi_coll_rank_(root) int root, // 聚集数据汇进程号
_In_ MPI_Comm comm // 通信子
);
#函数 MPI_Scatter() 和 MPI_Gather() 的范例
int main()
{
const int dataSize = 8 * 8;
const int localSize = 8;
int globalData[dataSize], localData[localSize], globalSum, i, comSize, comRank;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &comSize);
MPI_Comm_rank(MPI_COMM_WORLD, &comRank);
if (comRank == 0) // 初始化
for (i = 0; i < dataSize; globalData[i] = i, i++);
for (i = 0; i < localSize; localData[i++] = 0);
MPI_Scatter((void *)&globalData, localSize, MPI_INT, (void *)&localData, localSize, MPI_INT, 0, MPI_COMM_WORLD); // 分发数据
for (i = 0; i < localSize; localData[i++]++);
MPI_Barrier(MPI_COMM_WORLD); // 进程同步
MPI_Gather((void *)&localData, localSize, MPI_INT, (void *)&globalData, localSize, MPI_INT, 0, MPI_COMM_WORLD); // 聚集数据
for (i = globalSum = 0; i < dataSize; globalSum += globalData[i++]);
if (comRank == 0)
printf("\nSize = %d, Rank = %d, result = %d\n", comSize, comRank, globalSum);
MPI_Finalize();
return 0;// 输出结果:Size = 8, Rank = 0, result = 2080,表示 0 + 1 + 2 + …… + 63
}
MPI_Allgather
#全局聚集函数 MPI_Allgather(),将各进程的向量数据聚集为一个大向量,并分发到每个进程中,相当于各进程同步该大向量的各部分分量。相比于 MPI_Gather(),只是少了一个 root 参数。
#函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Allgather(
_In_opt_ _When_(sendtype == recvtype, _In_range_(!= , recvbuf)) const void* sendbuf,
_In_range_(>= , 0) int sendcount,
_In_ MPI_Datatype sendtype,
_Out_opt_ void* recvbuf,
_In_range_(>= , 0) int recvcount,
_In_ MPI_Datatype recvtype,
_In_ MPI_Comm comm
);
#函数 MPI_Scatter() 和 MPI_Allgather() 的范例
int main()
{
const int dataSize = 8 * 8;
const int localSize = 8;
int globalData[dataSize], localData[localSize], globalSum, i, comSize, comRank;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &comSize);
MPI_Comm_rank(MPI_COMM_WORLD, &comRank);
for (i = 0; i < dataSize; globalData[i] = i, i++);// 改动
for (i = 0; i < localSize; localData[i++] = 0);
MPI_Scatter((void *)&globalData, localSize, MPI_INT, (void *)&localData, localSize, MPI_INT, 0, MPI_COMM_WORLD); // 分发数据
for (i = 0; i < localSize; localData[i++]++);
MPI_Barrier(MPI_COMM_WORLD);
MPI_Allgather((void *)&localData, localSize, MPI_INT, (void *)&globalData, localSize, MPI_INT, MPI_COMM_WORLD); // 聚集数据,改动
for (i = globalSum = 0; i < dataSize; globalSum += globalData[i++]);
printf("\nSize = %d, rank = %d, result = %d\n", comSize, comRank, globalSum);// 改动
MPI_Finalize();
return 0;// 输出结果,八个进程乱序输出 2080
}
MPI_Scan
#前缀和函数 MPI_Scan(),将通信子内各进程的同一个变量参与前缀规约计算,并将得到的结果发送回每个进程,使用与函数 MPI_Reduce() 相同的操作类型
#函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Scan(
_In_opt_ _In_range_(!= , recvbuf) const void* sendbuf, // 指向参与规约数据的指针
_Out_opt_ void* recvbuf, // 指向接收规约结果的指针
_In_range_(>= , 0) int count, // 每个进程中参与规约的数据量
_In_ MPI_Datatype datatype, // 数据类型
_In_ MPI_Op op, // 规约操作类型
_In_ MPI_Comm comm // 通信子
);
#使用范例
int main(int argc, char **argv)
{
const int nProcess = 8, localSize = 8, globalSize = localSize * nProcess;
int globalData[globalSize], localData[localSize], sumData[localSize];
int comRank, comSize, i;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &comRank);
MPI_Comm_size(MPI_COMM_WORLD, &comSize);
if (comRank == 0)
for (i = 0; i < globalSize; globalData[i] = i, i++);
MPI_Scatter(globalData, localSize, MPI_INT, localData, localSize, MPI_INT, 0, MPI_COMM_WORLD);
for (i = 0; i < localSize; i++)
printf("%2d, ", localData[i]);
MPI_Scan(localData, sumData, localSize, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
for (i = 0; i < localSize; i++)
printf("%2d, ", sumData[i]);
MPI_Finalize();
return 0;
}
MPI_Reduce_Scatter
#规约分发函数 MPI_Reduce_Scatter(),将数据进行规约计算,结果分段分发到各进程中
#函数原型
_Pre_satisfies_(recvbuf != MPI_IN_PLACE) MPI_METHOD MPI_Reduce_scatter(
_In_opt_ _In_range_(!= , recvbuf) const void* sendbuf, // 指向输入数据的指针
_Out_opt_ void* recvbuf, // 指向接收数据的指针
_In_ const int recvcounts[], // 各进程接收规约结果的元素个数
_In_ MPI_Datatype datatype, // 数据类型
_In_ MPI_Op op, // 规约操作类型
_In_ MPI_Comm comm // 通信子
);
#使用范例
int main()
{
const int nProcess = 8, localSize = 8, globalSize = nProcess * localSize, countValue = 1;
int globalData[globalSize], localData[localSize], count[localSize], localSum[countValue], i, comSize, comRank;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &comSize);
MPI_Comm_rank(MPI_COMM_WORLD, &comRank);
if (comRank == 0)
for (i = 0; i < globalSize; globalData[i] = i, i++);
MPI_Scatter(globalData, localSize, MPI_INT, localData, localSize, MPI_INT, 0, MPI_COMM_WORLD);
for (i = 0; i < localSize; count[i++] = 1);
for (i = 0; i < localSize; i++)
printf("%3d, ", localData[i]);
MPI_Reduce_scatter(localData, localSum, count, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
for (i = 0; i < countValue; i++)
printf("%3d, ", localSum[i]);
MPI_Finalize();
return 0;
}