MPI Tutorial 2 - P2P Communication

P2P Communication

點對點通訊在平行程式中相當重要,由於每個處理器會獨立執行main(),定義的變數也是獨立的,若要將變數的值或是資料傳送給其他處理器,就會用到通訊的函式。

這個章節會介紹MPI_Send()及MPI_Recv()的應用

Example - Ring Program

ring.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <mpi.h>
#include <stdio.h>

int main(int agrc, char* argv[]) {
MPI_Init(NULL,NULL);
int size, rank;
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);

int value = 0;

if (rank == 0) {
value = 2021;
}
printf("Rank: %d, Value: %d\n",rank,value);
MPI_Barrier(MPI_COMM_WORLD);

if (rank != 0){ //All process should be ready for receiving the value first. Otherwise some process will send initial value to another one, instead of new value.
MPI_Recv(&value,1,MPI_FLOAT,rank-1,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
printf("Rank %d received the value from %d\n",rank,rank-1);
}

if (rank == 0){
MPI_Send(&value,1,MPI_FLOAT,rank+1,0,MPI_COMM_WORLD);
printf("Rank %d sent the value to %d\n",rank,rank+1);
}
else if(rank != size-1){
MPI_Send(&value,1,MPI_FLOAT,rank+1,0,MPI_COMM_WORLD);
printf("Rank %d sent the value to %d\n",rank,rank+1);
}
MPI_Barrier(MPI_COMM_WORLD);
printf("Program is end. Rank: %d, Value: %d\n",rank,value);
MPI_Finalize();
}

使用8個處理器的執行結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Rank: 1, Value: 0
Rank: 3, Value: 0
Rank: 6, Value: 0
Rank: 4, Value: 0
Rank: 5, Value: 0
Rank: 7, Value: 0
Rank: 2, Value: 0
Rank: 0, Value: 2021
Rank 0 sent the value to 1
Rank 1 received the value from 0
Rank 1 sent the value to 2
Rank 2 received the value from 1
Rank 2 sent the value to 3
Rank 3 received the value from 2
Rank 3 sent the value to 4
Rank 4 received the value from 3
Rank 4 sent the value to 5
Rank 5 received the value from 4
Rank 5 sent the value to 6
Rank 6 received the value from 5
Rank 6 sent the value to 7
Rank 7 received the value from 6
Program is end. Rank: 0, Value: 2021
Program is end. Rank: 1, Value: 2021
Program is end. Rank: 2, Value: 2021
Program is end. Rank: 3, Value: 2021
Program is end. Rank: 4, Value: 2021
Program is end. Rank: 5, Value: 2021
Program is end. Rank: 6, Value: 2021
Program is end. Rank: 7, Value: 2021

這個ring.c的範例主要是將 rank 0 的 value 變數依序傳遞給其餘的處理器,在開頭我們宣告 int value = 0,唯獨 rank 0 的 value 是 2021,宣告完後會先 print 每個 rank 各自的 value

接著你會看到MPI_Barrier(MPI_COMM_WORLD),這個函式會等待所有的處理器處理完才繼續執行後面的工作,為平行運算中的同步化。

最後每個處理器會使用MPI_Recv()來接收來自rank-1的資料,並存入value變數。如果沒有接收到資料,處理器會閒置直到接受資料為止。接著 rank 0 會發送自己value的值給 rank 1,然後 rank 1 會接收其值並存入自己的value變數,以此推類直到最後一個處理器。

MPI_Send()

1
2
3
4
5
6
7
MPI_Send(
void* data,
int count,
MPI_Datatype datatype,
int destination,
int tag,
MPI_Comm communicator)

第一個參數放要傳送的資料的記憶體位址,第二第三分別放傳送的資料數量及類型,destination是目標的 rank,tag可以是任意的正整數,用來標記。

MPI_Recv()

1
2
3
4
5
6
7
8
MPI_Recv(
void* data,
int count,
MPI_Datatype datatype,
int source,
int tag,
MPI_Comm communicator,
MPI_Status* status)

MPI_Send()類似,第一個參數放要存入的變數記憶體位址,唯一需要注意的是MPI_Recv()需要知道接收到的資料大小和類型。

MPI_Barrier()

1
MPI_Barrier(MPI_Comm communicator)

同步化所有的進程,等待所有處理器處理完分配的工作後,才會繼續執行後續的程序。

在範例中我們先執行MPI_Recv而不是MPI_Send,是為了避免 rank 0 外的處理器在得到上一個處理器傳送的value前先行傳送自己的value,這樣會導致 rank 0 的value無法依序傳遞到最後一個處理器。