组通信一般实现三个功能:通信、 同步和计算。
对于组通信按通信的方向的不同,可以分为以下三种:一对多通信、多对一通信和多对多通信。
(1)广播
MPI_BCAST完成从一个标识为root的进程将一条消息广播发送到组内的所有其它的进程。
示例代码:
int main(int argc, char* argv[])
{
int myid, size;
MPI_Init(&argc, &argv);
char message[124]="my name is rank0";
MPI_Status status;
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (myid == 0)//0进程作为根进程
{
MPI_Bcast(message, 124, MPI_CHAR, 0, MPI_COMM_WORLD);
}
else
{
//各进程打印接收到的消息
MPI_Bcast(message, 124, MPI_CHAR, 0,MPI_COMM_WORLD);
std::cout << "rank " << myid << " received message is:" << message << std::endl;
}
MPI_Finalize();
return 0;
}
打印结果如下:
(2)收集
在收集调用中,每个进程将其发送缓冲区中的消息发送到根进程,根进程根据发送进程的进程标识的序号,即进程的rank值,将它们各自的消息依次存放到自已的消息缓冲区中。
int main(int argc, char* argv[])
{
int myid, size;
MPI_Init(&argc, &argv);
char message[124];
char* recv=nullptr;
MPI_Status status;
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD, &size);
sprintf_s(message, "myname is rank%d\0", myid);
//进程0为root
if (myid == 0)
{
//为Root接收缓冲区分配空间
recv = (char*)malloc(size * 124 * sizeof(char));
}
//每个进程(包括根进程执行一次)执行一次发送操作
//根进程执行一次接收操作
MPI_Gather(message, 124, MPI_CHAR, recv, 124, MPI_CHAR, 0, MPI_COMM_WORLD);
//接收并打印信息
if (myid == 0)
{
for (int i = 0; i < size; i++)
{
for (int j = i * 124; j < i * 124 + 124; j++)
std::cout << recv[j];
std::cout << std::endl;
}
}
MPI_Finalize();
return 0;
}
执行结果如下:
MPI_GATHERV和MPI_GATHER的功能类似,也完成数据收集的功能。但是它可以从不同的进程接收不同数量的数据。为此,接收数据元素的个数recvcounts是一个数组,用于指明从不同的进程接收的数据元素的个数。
示例代码如下:
int main(int argc, char* argv[])
{
int* send; //发送数组
int* recv=nullptr; //接收数组
int* buf=nullptr; //偏移数组
int* rcount=nullptr;//记录各个发送数组中元素的个数
int n; //发送数组元素个数
int m=0; //各进程发送的数据个数总和
int myid, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
MPI_Comm_size(MPI_COMM_WORLD, &size);
srand((unsigned)(time(NULL) + myid));
n = rand() % 8 + myid;
if (myid == 0)
{
buf = (int*)malloc(size * sizeof(int));
rcount = (int*)malloc(size * sizeof(int));
}
//将各进程发送的数据个数n(包括root进程)发送给根进程
MPI_Gather(&n, 1, MPI_INT, rcount, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Barrier(MPI_COMM_WORLD);//等待各个进程全部将n发送给root
//为发送数组分配数据并赋值
send = (int*)malloc((n+1) * sizeof(int));
for (int i = 0; i < n; i++)
{
send[i] = rand()%9;
}
if (myid == 0)
{
//计算接收数据个数的总和
for (int i = 0; i < size; i++)
m += rcount[i];
//为接收数组分配内存
recv = (int*)malloc(m * sizeof(int));
//设置偏移数组
buf[0] = 0;
for (int i = 1; i < size; i++)
buf[i] = buf[i - 1] + rcount[i - 1];
}
/*
将各进程需发送的数据和偏移数组发送给root
*/
MPI_Gatherv(send, n, MPI_INT, recv, rcount, buf, MPI_INT, 0, MPI_COMM_WORLD);
//打印各进程发送的数据
for (int i = 0; i < n; i++)
{
std::cout << send[i] << " ";
}
std::cout << std::endl;
//打印根进程接收到的数据
for (int i = 0; i < m; i++)
{
std::cout << recv[i] << " ";
}
MPI_Finalize();
return 0;
}
将进程数设置为5,打印结果如下: