1. 问题描述
创建np
个进程,使第iam
个进程向iam+1
个进程发送消息,第np-1
个进程向第0
个进程发送消息,行成一个环形结构
2. 解决思路
申明一个子程序
/*************************************************
Function: ring
Description: 实现进程间环形通讯
Input: m: 发送的消息
comm:MPI通讯域
np: 进程数
iam:当前进程号
Output: n:接收的消息
Return: void
*************************************************/
void ring(int m, int *n, MPI_Comm comm, int np, int iam);
2.1 定位前后进程
已知第iam
个进程,定位前一个进程。为了防止第0
个进程减1
后得到负数,需要加上np
front = (np + iam - 1) % np;
已知第iam
个进程,定位下一个进程,为了使第np-1
个进程的下一个进程为0
,需要取模
next = (iam + 1) % np;
2.2 消息发送
思路一
使用MPI_Send
,MPI_Recv
。这对函数是阻塞式操作,为了防止死锁,需要有进程先发送消息。最直观的思路是0号进程先发送再接收,其他进程先接收再发送。但这样几乎没有并行性,效率太低;考虑让偶数号进程先发送,奇数号进程先接收。
if (iam % 2 == 0){ // 让做一半进程先发送,一半进程再接收
MPI_Send(&m, 1, MPI_INT, next, 1, comm);
MPI_Recv(n, 1, MPI_INT, front, 1, comm, &status);
}
else{ //一定要有进程在发送之前要先接收偶数进程的消息
MPI_Recv(n, 1, MPI_INT, front, 1, comm, &status);
MPI_Send(&m, 1, MPI_INT, next, 1, comm);
}
思路二
使用MPI_Isend
,MPI_Irecv
。这对函数是非阻塞式操作,因此不会出现死锁,但是可能有的进程消息还没有收到就直接退出的情况,因此需要使用MPI_Wait
同步进程。
同步的思路很灵活,既可以等待发送和接收都完成,也可以只等待接收操作完成
MPI_Status status[2];
MPI_Request reqs[2];
MPI_Isend(&m, 1, MPI_INT, next, 1, comm, &reqs[0]);
MPI_Irecv(n, 1, MPI_INT, front, 1, comm, &reqs[1]);
//MPI_Waitall(2, reqs, status); // 等待发送和接收都完成
MPI_Wait(&reqs[1], &status[1]); // 仅等待接收完成
思路三
使用MPI_Sendrecv
,该函数整合send
和recv
操作,认为send
和recv
操作组合是阻塞的,而send
和recv
之间又是没有顺序的
MPI_Sendrecv(&m, 1, MPI_INT, next, 1, \
n, 1, MPI_INT, front, 1, comm, &status);
2.3 主程序
int main(int argc, char *argv[])
{
// my begin
int rank;
int numtasks;
MPI_Init(&argc, &argv); /*Initializes MPI calls*/
MPI_Comm_rank(MPI_COMM_WORLD, &rank); /* obtains the rank of current MPI process */
MPI_Comm_size(MPI_COMM_WORLD, &numtasks); /* obtains the total number of MPI processes */
// my function
int m = rank, n = 100; //m:发送数据,n:接收数据
ring(m, &n, MPI_COMM_WORLD, numtasks, rank);
cout << "My rank:" << rank << "\tMy n:" << n;
// my end
MPI_Finalize(); /*Finalizes MPI calls */
return 0;
}