之前的点到点通信是两个进程之间的相互通信,而集合通信则是在一个通信子内所有进程参与的通信。

广播操作MPI_Bcast

广播操作就是一个进程将相同的数据发送到同通信子中的其他进程中,它的一个重要通途就是将同一份输入数据发送给不同的进程方便进行进一步的并行计算。

自己顺便也做了张图:

下面是个广播操作的代码:

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
#include "mpi.h"
#include <stdio.h>

int main(int argc, char ** argv)
{
int rank, nproc, value;

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

do
{
if (rank == 0)
{
fprintf(stderr, "Please enter a value, -1 means finish:\n");
scanf("%d", &value);
}

MPI_Bcast(&value, 1, MPI_INT, 0, MPI_COMM_WORLD);
fprintf(stderr, "Process %d got %d\n", rank, value);
}while(value >= 0);

MPI_Finalize();

return 0;
}

执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[zjshao@master 4-1-1]$ mpicc bcast.c -o bcast.x
[zjshao@master 4-1-1]$ mpiexec -n 4 -host node01 bcast.x
Please enter a value, -1 means finish:
6
Process 0 got 6
Please enter a value, -1 means finish:
Process 1 got 6
Process 2 got 6
Process 3 got 6
7
Process 0 got 7
Please enter a value, -1 means finish:
Process 1 got 7
Process 2 got 7
Process 3 got 7
-1
Process 0 got -1
Process 1 got -1
Process 2 got -1
Process 3 got -1
[zjshao@master 4-1-1]

可见广播操作所有的进程都要执行MPI_Bcast()函数,但是执行的操作却不同。跟进程通过调用MPI_Bcast()将数据的副本发送出去,其他进程调用MPI_Bcast()则启动接收操作,将数据数组填满。
通过第一个输入可知道,在根进程阻塞的时候,其他的进程执行到MPI_Bcast也阻塞来等待跟进程发来的数据。

发散操作MPI_Scatter

与广播操作发送相同的数据到其他进程不同,发散操作则是将一个数组进行分散然后将不同的部分分发到其他进程中。如下图:

先看看发散函数的函数原型:

1
2
3
4
5
6
7
8
MPI_Scatter(void* send_data,
int send_count,
MPI_Datatype send_datatype,
void* recv_data,
int recv_count,
MPI_Datatype recv_datatype,
int root,
MPI_Comm communicator)

send_data是在根进程中的要发送数据的首地址。
第二个和第三个参数则描述了如何向其他进程发送数据,例如如果send_datatypeMPI_INTsend_count为2,就将前两个int数据发送到第0个进程,以此类推。。。

下面直接上代码好了:

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
#include "mpi.h"
#include <stdio.h>

#define SIZE 4

int main(int argc, char ** argv)
{
int rank, nproc, scnt, rcnt, src;
float sb[SIZE][SIZE] = {
{1.0, 2.0, 3.0, 4.0},
{5.0, 6.0, 7.0, 8.0},
{9.0, 10.0, 11.0, 12.0},
{13.0, 14.0, 15.0, 16.0},
};
float rb[SIZE];
MPI_Comm comm = MPI_COMM_WORLD;

MPI_Init(&argc, &argv);
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &nproc);

if (nproc == SIZE)
{
src = 1;
scnt = SIZE;
rcnt = SIZE;

MPI_Scatter(sb, scnt, MPI_FLOAT,
rb, rcnt, MPI_FLOAT,
src, comm);
rb, rcnt, MPI_FLOAT,
src, comm);
fprintf(stderr, "rank = %d Results: %f %f %f %f\n", rank, rb[0], rb[1], rb[2], rb[3]);
}
else
{
fprintf(stderr, "Must specify %d processors. Terminating.\n", SIZE);
}

MPI_Finalize();

return 0;
}

执行结果:
1
2
3
4
5
6
[zjshao@master 4-1-2]$ mpiexec -n 4 -host node01 scatter.x
rank = 1 Results: 5.000000 6.000000 7.000000 8.000000
rank = 2 Results: 9.000000 10.000000 11.000000 12.000000
rank = 3 Results: 13.000000 14.000000 15.000000 16.000000
rank = 0 Results: 1.000000 2.000000 3.000000 4.000000
[zjshao@master 4-1-2]$

在上面的例子中send_count=4,也就是说进程1将sb中的前4个float发送给进程0,5~8发送给自己,以此类推。。。

Comments