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;
}