MPI学习--阻塞通信之就绪通信模式和同步通信模式
本篇对阻塞通信中的就绪通信模式和同步通信模式进行小结。
就绪通信模式
就绪模式的发送方式与同步和标准发送完全一致,不一样的是就绪通信模式是:
仅当对方的接收操作启动并且准备就绪时,才可发生数据,并不是MPI会自动判断是否就绪(他没那么智能?)而是告诉MPI对方以准备就绪接收你发的消息,如果你骗了MPI,MPI会不爽,然后报错。
基于你告诉MPI的这个信息,就可以避免一些列的缓冲操作以及收发双方的握手操作,是的MPI环境可对通信做更细致的优化以提高通信效率。
需要注意的是:
还是通过代码来说明的清楚。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
56
57
58
59
60
int main(int argc, char ** argv)
{
int rank, size, i;
int buffer[10];
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm comm = MPI_COMM_WORLD;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &size);
fprintf(stderr, "size = %d\n", size);
if (size < 2)
{
fprintf(stderr, "Please run with two processes.\n");
fflush(stderr);
MPI_Finalize();
return 0;
}
else if(rank == 0)
{
for (i = 0; i < 10; ++i)
{
buffer[i] = -1;
}
MPI_Recv(buffer, 10, MPI_INT, 1, 123, comm, &status);
// Check.
for (i = 0; i < 10; ++i)
{
if (buffer[i] != i)
{
fprintf(stderr,
"Error: buffer[%d]=%d but is expected to be %d\n",
i, buffer[i], i);
}
}
for (i = 0; i < 10; ++i)
{
fprintf(stderr, "buffer[%d]=%d but is expected to be %d\n", i, buffer[i], i);
}
fflush(stderr);
}
else if (rank == 1)
{
for (i = 0; i < 10; ++i)
{
buffer[i] = i;
}
MPI_Rsend(buffer, 10, MPI_INT, 0, 123, comm);
}
MPI_Finalize();
return 0;
}
编译后能够在两个进程中正确运行:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15[zjshao@master ch02]$ mpicc rsendrecv.c -o rsendrecv.x
[zjshao@master ch02]$ mpiexec -n 2 -host node01 rsendrecv.x
size = 2
size = 2
buffer[0]=0 but is expected to be 0
buffer[1]=1 but is expected to be 1
buffer[2]=2 but is expected to be 2
buffer[3]=3 but is expected to be 3
buffer[4]=4 but is expected to be 4
buffer[5]=5 but is expected to be 5
buffer[6]=6 but is expected to be 6
buffer[7]=7 but is expected to be 7
buffer[8]=8 but is expected to be 8
buffer[9]=9 but is expected to be 9
[zjshao@master ch02]$
假如我们欺骗了MPI
我们现在通过阻塞函数在接收端为做好准备时候进行就绪消息传递: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...
{
for (i = 0; i < 10; ++i)
{
buffer[i] = -1;
}
MPI_Barrier(comm);
MPI_Recv(buffer, 10, MPI_INT, 1, 123, comm, &status);
// Check.
for (i = 0; i < 10; ++i)
{
if (buffer[i] != i)
{
fprintf(stderr,
"Error: buffer[%d]=%d but is expected to be %d\n",
i, buffer[i], i);
}
}
for (i = 0; i < 10; ++i)
{
fprintf(stderr, "buffer[%d]=%d but is expected to be %d\n", i, buffer[i], i);
}
fflush(stderr);
}
else if (rank == 1)
{
for (i = 0; i < 10; ++i)
{
buffer[i] = i;
}
MPI_Rsend(buffer, 10, MPI_INT, 0, 123, comm);
MPI_Barrier(comm);
}
...
输出是啥样的呢:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[zjshao@master ch02]$ mpicc rsendrecv.c -o rsendrecv.x
[zjshao@master ch02]$ mpiexec -n 2 -host node01 rsendrecv.x
size = 2
size = 2
===================================================================================
= BAD TERMINATION OF ONE OF YOUR APPLICATION PROCESSES
= PID 10073 RUNNING AT node01
= EXIT CODE: 11
= CLEANING UP REMAINING PROCESSES
= YOU CAN IGNORE THE BELOW CLEANUP MESSAGES
===================================================================================
YOUR APPLICATION TERMINATED WITH THE EXIT STRING: Segmentation fault (signal 11)
This typically refers to a problem with your application.
Please see the FAQ page for debugging suggestions
[zjshao@master ch02]$
1 | [zjshao@master ch02]$ mpicc rsendrecv.c -o rsendrecv.x |
MPI输出这些是啥意思呢?我大概翻译下:
MPI:”你骗我!老子不干了。”
同步通信模式
与就绪通信模式不同,同步的发送动作什么时候做都行,即使接收方没有准备好也没有关系。如果接收方没有准备好咋办呢?等。即发送端需等待接收端的接受动作发起并开始接受数据之后才能够返回。
发送动作的结束意味着:
- 发送缓冲区已经可以用于其它用途
- 接收端已经接受了(部分)数据了
同步通信协议
发送端首先向接收端发起一个请求发送消息的申请,接收端的MPI环境会将这个请求保存下来,然后带相应的接受动作启动之后为其返回一个信息发送许可,发送端据此信息再执行实际的消息发送。
这里由于阻塞同步通信比较简单,而且代码基本上跟标准模式相同,就不写例子了。
对这样的程序把所有的发送操作替换为同步通信模式时仍可保持正确。这样的程序可移植性最好,但是性能不一定最优。好的解决办法就是反阻塞通信。