1 常用函数
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
MPI_Barrier(MPI_COMM_WORLD);
MPI_Abort(MPI_Comm comm, int errorcode);
MPI_Send(
void* data,
int count,
MPI_Datatype datatype,
int destination,
int tag,
MPI_Comm communicator);
MPI_Recv(
void* data,
int count,
MPI_Datatype datatype,
int source,
int tag,
MPI_Comm communicator,
MPI_Status* status);
MPI_Get_count(
MPI_Status* status,
MPI_Datatype datatype,
int* count);
MPI_Probe(
int source,
int tag,
MPI_Comm comm,
MPI_Status* status);
2 MPI_Status 结构体
MPI_Recv
将 MPI_Status
结构体的地址作为参数(可使用 MPI_STATUS_IGNORE
忽略)。 如果将 MPI_Status
结构体传递给 MPI_Recv
函数,则操作完成后将在该结构体中填充有关接收操作的其他信息。
三个主要的信息包括:
- 发送端秩: 发送端的秩存储在结构体的
MPI_SOURCE
元素中。也就是说,声明一个MPI_Status stat
变量,则可以通过stat.MPI_SOURCE
访问秩。 - 消息的标签: 消息的标签可以通过结构体的
MPI_TAG
元素访问(类似于MPI_SOURCE
)。 - 消息的长度:消息的长度在结构体中没有预定义的元素。必须使用
MPI_Get_count
找出消息的长度。
MPI_Get_count(
MPI_Status* status,
MPI_Datatype datatype,
int* count)
在 MPI_Get_count
函数中,使用者需要传递 MPI_Status
结构体,消息的 datatype
(数据类型),并返回 count
。 变量 count
是已接收的 datatype
元素的数目。
2.1 MPI_Status
结构体查询的示例
程序将随机数量的数字发送给接收端,然后接收端找出发送了多少个数字。
MPI_Status status;
const int MAX_NUMBERS = 100;
int numbers[MAX_NUMBERS];
int number_amount;
if (world_rank == 0) {
srand(time(NULL));
number_amount = (rand() / (float)RAND_MAX) * MAX_NUMBERS;
MPI_Send(numbers, number_amount, MPI_INT, 1, 0, MPI_COMM_WORLD);
printf("0 sent %d numbers to 1\n", number_amount);
} else if (world_rank == 1) {
MPI_Status status;
MPI_Recv(numbers, MAX_NUMBERS, MPI_INT, 0, 0, MPI_COMM_WORLD,&status);
MPI_Get_count(&status, MPI_INT, &number_amount);
printf("1 received %d numbers from 0. Message source = %d, "
"tag = %d\n",
number_amount, status.MPI_SOURCE, status.MPI_TAG);
}
2.2 MPI_Probe
探测接收消息的内容
MPI_Probe(int source,int tag,MPI_Comm comm,MPI_Status* status)
,在接受之前探测消息,从而决定如何接收。
可将 MPI_Probe
视为 MPI_Recv
,除了不接收消息外,它们执行相同的功能。 与 MPI_Recv
类似,MPI_Probe
将阻塞具有匹配标签和发送端的消息。 当消息可用时,它将填充 status 结构体。 然后,可以使用 MPI_Recv
接收实际的消息。
else if (world_rank == 1) {
MPI_Status status;
MPI_Probe(0, 0, MPI_COMM_WORLD, &status);
MPI_Get_count(&status, MPI_INT, &number_amount);
int *number_buf = (int *)malloc(sizeof(int) * number_amount);
MPI_Recv(number_buf, number_amount, MPI_INT, 0, 0,
MPI_COMM_WORLD, MPI_STATUS_IGNORE);
printf("1 dynamically received %d numbers from 0.\n",
number_amount);
free(number_buf);
}
3 点对点通信 - 随机游走
随机游走的基本问题定义如下: 给定 Min,Max 和随机游走器 W,让游走器 W 向右以任意长度的 S 随机移动。 如果该过程越过边界,它就会绕回。 W 一次只能左右移动一个单位。
尽管程序本身是非常基础的,但是并行化的随机游走可以模拟各种并行程序的行为。 具体内容以后再说。 现在,让我们概述一下如何并行化随机游走问题。
3.1 随机游走问题的并行化
在许多并行程序的应用中,首要任务是在各个进程之间划分域。 随机行走问题的一维域大小为 Max - Min + 1(因为游走器包含 Max 和 Min)。 假设游走器只能采取整数大小的步长,我们可以轻松地将域在每个进程中划分为大小近乎相等的块。 例如,如果 Min 为 0,Max 为 20,并且我们有四个进程,则将像这样拆分域。
前三个进程拥有域的五个单元,而最后一个进程则拥有最后五个单元并且再加上一个剩余的单元。 一旦对域进行了分区,应用程序将初始化游走器。 如前所述,游走器将以步长 S 进行总步数随机的游走。 例如,如果游走器在进程 0(使用先前的分解域)上进行了移动总数为 6 的游走,则游走器的执行将如下所示:
- 游走器的步行长度开始增加。但是,当它的值达到 4 时,它已到达进程 0 的边界。因此,进程 0 必须与进程 1 交流游走器的信息。
- 进程 1 接收游走器,并继续移动,直到达到移动总数 6。然后,游走器可以继续进行新的随机移动。
在此示例中,W 仅需从进程 0 到进程 1 进行一次通信。 但是,如果