本文也是通过实例来理解总结一下非阻塞通信中的非重复的就绪通信和同步通信模式。

这里也没啥,就是通过两个例子来学习一下非阻塞通信中的就绪通信和同步通信。

非重复的就绪通信

这里的就绪通信同阻塞通信中的就绪通信一样,都是告诉MPI接收方已经准备就绪,如果检测到并没有准备就绪则MPI就会报错。其他地方就是非阻塞通信与阻塞通信之间的区别了。

下面我就写了个例子来看看MPI_Isend()MPI_Irecv()之间和MPI_Isend()MPI_Recv()之间的通信。
下面的例子是不同进程之间从左边的进程接收数据并将当前进程的数据发送到右边的进程,因此代码中不需要对进程号进行判断处理。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "mpi.h"
#include <stdio.h>

#define BUFSIZE 5

int main(int argc, char ** argv)
{
int myid, nproc, left, right, i, j;
int rbuf[BUFSIZE], sbuf[BUFSIZE];
char pstr[BUFSIZE*(sizeof(int) + 8) + 100];
MPI_Request rreq, sreq;
MPI_Status status;

MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);

// Get source and target process number.
right = (myid + 1)%nproc;
left = myid - 1;
if (left < 0)
{
left = nproc - 1;
}

// Data to be sent.
for (i = 0; i < BUFSIZE; ++i)
{
sbuf[i] = myid*BUFSIZE + i;
}

// Receive data from left process.
MPI_Irecv(rbuf, BUFSIZE, MPI_INT, left, 123, MPI_COMM_WORLD, &rreq);

MPI_Barrier(MPI_COMM_WORLD);

// Send data to right process.
MPI_Irsend(sbuf, BUFSIZE, MPI_INT, right, 123, MPI_COMM_WORLD, &sreq);

MPI_Wait(&rreq, &status);
MPI_Wait(&sreq, &status);

// Info output.
sprintf(pstr, "proc: %d", myid);
for (j = 0; j < BUFSIZE-1; ++j)
{
sprintf(pstr, "%s, rbuf[%d]=%d", pstr, j, rbuf[j]);
}
sprintf(pstr, "%s, rbuf[%d]=%d\n", pstr, j, rbuf[j]);
fprintf(stderr, "%s", pstr);

MPI_Finalize();

return 0;
}

编译执行的输出信息:
1
2
3
4
5
6
[zjshao@master 2-2]$ mpiexec -n 4 -host node01 irsendirecv.x 
proc: 1, rbuf[0]=0, rbuf[1]=1, rbuf[2]=2, rbuf[3]=3, rbuf[4]=4
proc: 2, rbuf[0]=5, rbuf[1]=6, rbuf[2]=7, rbuf[3]=8, rbuf[4]=9
proc: 3, rbuf[0]=10, rbuf[1]=11, rbuf[2]=12, rbuf[3]=13, rbuf[4]=14
proc: 0, rbuf[0]=15, rbuf[1]=16, rbuf[2]=17, rbuf[3]=18, rbuf[4]=19
[zjshao@master 2-2]$

需要注意的是,即使是异步方式的消息通信,还是要求接收端在发送端启动之前启动,所以代码中在所有进程进行接收动作以后设置了一个全局同步动作,确保所有进行接收动作就绪后再启动发送动作。

上述代码将非阻塞通信的接收动作MPI_Irecv换成阻塞通信的MPI_Recv(),但是要注意的是这个时候会出现死锁问题,以为这时的接收动作已经不是非阻塞的,他不会立即返回,因此如果将发送动作放在接收动作后面就会使得接收一直无法返回造成死锁,效果如下:

1
2
3
4
5
6
7
8
[zjshao@master 2-2]$ mpiexec -n 4 -host node01 irsendrecv.x 
proc:1 trying MPI_Recv from proc:0...
proc:2 trying MPI_Recv from proc:1...
proc:3 trying MPI_Recv from proc:2...
proc:0 trying MPI_Recv from proc:3...
[mpiexec@master.cluster] Sending Ctrl-C to processes as requested
[mpiexec@master.cluster] Press Ctrl-C again to force abort
[zjshao@master 2-2]$

非重复的同步通信

同步通信的特点就是如果接收未完成,发送端就会。下面是例子代码,基本上与上面的例子相同:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "mpi.h"
#include <stdio.h>

#define BUFSIZE 5

int main(int argc, char ** argv)
{
int myid, nproc, left, right, i, j;
int rbuf[BUFSIZE], sbuf[BUFSIZE];
char pstr[BUFSIZE*(sizeof(int) + 8) + 100];
MPI_Request rreq, sreq;
MPI_Status status;

MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);

// Get source and target process number.
right = (myid + 1)%nproc;
left = myid - 1;
if (left < 0)
{
left = nproc - 1;
}

// Data to be sent.
for (i = 0; i < BUFSIZE; ++i)
{
sbuf[i] = myid*BUFSIZE + i;
}

// Receive data from left process.
MPI_Irecv(rbuf, BUFSIZE, MPI_INT, left, 123, MPI_COMM_WORLD, &rreq);

// Send data to right process.
MPI_Issend(sbuf, BUFSIZE, MPI_INT, right, 123, MPI_COMM_WORLD, &sreq);

MPI_Wait(&rreq, &status);
MPI_Wait(&sreq, &status);

// Info output.
sprintf(pstr, "proc: %d", myid);
for (j = 0; j < BUFSIZE-1; ++j)
{
sprintf(pstr, "%s, rbuf[%d]=%d", pstr, j, rbuf[j]);
}
sprintf(pstr, "%s, rbuf[%d]=%d\n", pstr, j, rbuf[j]);
fprintf(stderr, "%s", pstr);

MPI_Finalize();

return 0;
}

执行结果:
1
2
3
4
5
6
[zjshao@master 2-2]$ mpiexec -n 4 -host node01  issendirecv.x
proc: 1, rbuf[0]=0, rbuf[1]=1, rbuf[2]=2, rbuf[3]=3, rbuf[4]=4
proc: 2, rbuf[0]=5, rbuf[1]=6, rbuf[2]=7, rbuf[3]=8, rbuf[4]=9
proc: 0, rbuf[0]=15, rbuf[1]=16, rbuf[2]=17, rbuf[3]=18, rbuf[4]=19
proc: 3, rbuf[0]=10, rbuf[1]=11, rbuf[2]=12, rbuf[3]=13, rbuf[4]=14
[zjshao@master 2-2]$

上面的代码接收操作放在发送操作之前,我们将顺序倒过来仍然可以正确执行,因为非阻塞通信会立即返回,让MPI异步等待,这样就不会造成死锁了。

Comments