我在研究mpi send和recv函数的例子时有一个疑问
I have a question while studying the example of mpi send and recv function
下面的代码在学习发送和响应函数的同时展示了一个pingpong的例子。
但是我不明白 parter_rank .
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
const int PING_PONG_LIMIT = 10;
// Initialize the MPI environment
MPI_Init(NULL, NULL);
// Find out rank, size
int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
int world_size;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
// We are assuming 2 processes for this task
if (world_size != 2) {
fprintf(stderr, "World size must be two for %s\n", argv[0]);
MPI_Abort(MPI_COMM_WORLD, 1);
}
int ping_pong_count = 0;
int partner_rank = (world_rank + 1) % 2;
while (ping_pong_count < PING_PONG_LIMIT) {
if (world_rank == ping_pong_count % 2) {
// Increment the ping pong count before you send it
ping_pong_count++;
MPI_Send(&ping_pong_count, 1, MPI_INT, partner_rank, 0, MPI_COMM_WORLD);
printf("%d sent and incremented ping_pong_count %d to %d\n", world_rank, ping_pong_count, partner_rank);
} else {
MPI_Recv(&ping_pong_count, 1, MPI_INT, partner_rank, 0, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
printf("%d received ping_pong_count %d from %d\n",
world_rank, ping_pong_count, partner_rank);}
}
MPI_Finalize();
}
Q1。很明显,上面的MPI_Comm_rank定义为world_rank,但是我不明白下面的partner_rank是什么意思。
这两个排名有什么区别?
Q2.When没看懂 if (world_rank == ping_pong_count % 2)
我们不能只写“rank ==0”和“rank ==1”吗?你为什么把算术运算符放在那里?
非常感谢您的评论。
这种看似不必要的算法和引入额外秩变量的原因是代码对称性。也就是说,由于最常见的 MPI 程序类型是作为 同一程序 的 多个副本 运行的程序,对称代码意味着代码不没有条件语句将排名与特定常量进行比较。为什么这很重要?因为它让代码更灵活,更容易理解。
比较 two-rank MPI ping-pong 程序的以下两个等效规范:
规格 1
- 排名 0 向排名 1 发送消息
- 排名 1 然后将消息发送回排名 0
- 过程重复N次
本规范在 C-like 伪代码中的实现可以是:
loop N times {
if (rank == 0) {
MPI_Send to 1
MPI_Recv from 1
}
else if (rank == 1) {
MPI_Recv from 0
MPI_Send to 0
}
}
规格 2
- 每个级别从另一个级别接收
- 每个等级然后发送到另一个等级
- 过程重复N-1次
- 另外,rank 0 通过发送给其他 rank 来启动进程,并通过从其他 rank 接收最后一条消息来结束进程
可能的伪代码实现:
other_rank = 1 - rank
if (rank == 0) {
MPI_Send to other_rank
}
loop N-1 times {
MPI_Recv from other_rank
MPI_Send to other_rank
}
if (rank == 0) {
MPI_Recv from other_rank
}
第二个规范(及其实现)乍一看可能更复杂,但事实并非如此。它的优势在于它是本地的——它不会给出特定等级必须做什么的全球规定。相反,它给出了系统中任何等级的作用的处方,仅在过程开始和结束时破坏对称性,因为必须启动链。
如果我们想要扩展系统并且没有两个,而是三个等级在一个环中传递消息怎么办?我们希望等级 0 将消息传递给等级 1,然后等级 1 将其传递给等级 2,等级 2 又将其传递回等级 0。扩展规范 1 导致:
- 排名 0 发送到排名 1
- 等级 0 从等级 2 接收
- 等级 1 从等级 0 接收
- 排名 1 发送到排名 2
- 等级 2 从等级 1 接球
- 排名 2 发送到排名 0
在伪代码中:
loop N times {
if (rank == 0) {
MPI_Send to 1
MPI_Recv from 2
}
else if (rank == 1) {
MPI_Recv from 0
MPI_Send to 2
}
else if (rank == 2) {
MPI_Recv from 1
MPI_Send to 0
}
}
尝试将其扩展到四级,然后再扩展到五级。
另一方面,规范 2 自然扩展到三、四...实际上,扩展到任意数量的等级:
- 每个级别从上一个级别接收
- 每个级别发送到下一个级别
- 等级 0 通过发送第一条消息启动进程,并通过接收最后一条消息结束进程
在伪代码中:
prev_rank = (rank - 1 + #ranks) % #ranks
next_rank = (rank + 1) % #ranks
if (rank == 0) {
MPI_Send to next_rank
}
loop N-1 times {
MPI_Recv from prev_rank
MPI_Send to next_rank
}
if (rank == 0) {
MPI_Recv from prev_rank
}
值得注意的是,规范 2 只不过是此通用规范的一个特定情况,#ranks
等于 2
。在这种情况下,prev_rank
和 next_rank
都等于 (rank + 1) % 2
,即是一个相同的等级。此外,当排名取 0
或 1
.
的值时,(rank + 1) % 2
和 1 - rank
相同
我希望您现在明白背后的动机不是将特定操作硬编码到特定级别,而是使用本地算术来确定要做什么。在您的情况下,每个偶数 ping 消息值都按等级 0 递增,每个奇数 ping 消息值按等级 1 递增,但是如果您将它扩展到一个等级环怎么办? if (rank == ping_value % #ranks) ping_value++;
有点做正确的事并且适用于任何数量的等级。
下面的代码在学习发送和响应函数的同时展示了一个pingpong的例子。 但是我不明白 parter_rank .
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
const int PING_PONG_LIMIT = 10;
// Initialize the MPI environment
MPI_Init(NULL, NULL);
// Find out rank, size
int world_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
int world_size;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
// We are assuming 2 processes for this task
if (world_size != 2) {
fprintf(stderr, "World size must be two for %s\n", argv[0]);
MPI_Abort(MPI_COMM_WORLD, 1);
}
int ping_pong_count = 0;
int partner_rank = (world_rank + 1) % 2;
while (ping_pong_count < PING_PONG_LIMIT) {
if (world_rank == ping_pong_count % 2) {
// Increment the ping pong count before you send it
ping_pong_count++;
MPI_Send(&ping_pong_count, 1, MPI_INT, partner_rank, 0, MPI_COMM_WORLD);
printf("%d sent and incremented ping_pong_count %d to %d\n", world_rank, ping_pong_count, partner_rank);
} else {
MPI_Recv(&ping_pong_count, 1, MPI_INT, partner_rank, 0, MPI_COMM_WORLD,
MPI_STATUS_IGNORE);
printf("%d received ping_pong_count %d from %d\n",
world_rank, ping_pong_count, partner_rank);}
}
MPI_Finalize();
}
Q1。很明显,上面的MPI_Comm_rank定义为world_rank,但是我不明白下面的partner_rank是什么意思。 这两个排名有什么区别?
Q2.When没看懂 if (world_rank == ping_pong_count % 2)
我们不能只写“rank ==0”和“rank ==1”吗?你为什么把算术运算符放在那里?
非常感谢您的评论。
这种看似不必要的算法和引入额外秩变量的原因是代码对称性。也就是说,由于最常见的 MPI 程序类型是作为 同一程序 的 多个副本 运行的程序,对称代码意味着代码不没有条件语句将排名与特定常量进行比较。为什么这很重要?因为它让代码更灵活,更容易理解。
比较 two-rank MPI ping-pong 程序的以下两个等效规范:
规格 1
- 排名 0 向排名 1 发送消息
- 排名 1 然后将消息发送回排名 0
- 过程重复N次
本规范在 C-like 伪代码中的实现可以是:
loop N times {
if (rank == 0) {
MPI_Send to 1
MPI_Recv from 1
}
else if (rank == 1) {
MPI_Recv from 0
MPI_Send to 0
}
}
规格 2
- 每个级别从另一个级别接收
- 每个等级然后发送到另一个等级
- 过程重复N-1次
- 另外,rank 0 通过发送给其他 rank 来启动进程,并通过从其他 rank 接收最后一条消息来结束进程
可能的伪代码实现:
other_rank = 1 - rank
if (rank == 0) {
MPI_Send to other_rank
}
loop N-1 times {
MPI_Recv from other_rank
MPI_Send to other_rank
}
if (rank == 0) {
MPI_Recv from other_rank
}
第二个规范(及其实现)乍一看可能更复杂,但事实并非如此。它的优势在于它是本地的——它不会给出特定等级必须做什么的全球规定。相反,它给出了系统中任何等级的作用的处方,仅在过程开始和结束时破坏对称性,因为必须启动链。
如果我们想要扩展系统并且没有两个,而是三个等级在一个环中传递消息怎么办?我们希望等级 0 将消息传递给等级 1,然后等级 1 将其传递给等级 2,等级 2 又将其传递回等级 0。扩展规范 1 导致:
- 排名 0 发送到排名 1
- 等级 0 从等级 2 接收
- 等级 1 从等级 0 接收
- 排名 1 发送到排名 2
- 等级 2 从等级 1 接球
- 排名 2 发送到排名 0
在伪代码中:
loop N times {
if (rank == 0) {
MPI_Send to 1
MPI_Recv from 2
}
else if (rank == 1) {
MPI_Recv from 0
MPI_Send to 2
}
else if (rank == 2) {
MPI_Recv from 1
MPI_Send to 0
}
}
尝试将其扩展到四级,然后再扩展到五级。
另一方面,规范 2 自然扩展到三、四...实际上,扩展到任意数量的等级:
- 每个级别从上一个级别接收
- 每个级别发送到下一个级别
- 等级 0 通过发送第一条消息启动进程,并通过接收最后一条消息结束进程
在伪代码中:
prev_rank = (rank - 1 + #ranks) % #ranks
next_rank = (rank + 1) % #ranks
if (rank == 0) {
MPI_Send to next_rank
}
loop N-1 times {
MPI_Recv from prev_rank
MPI_Send to next_rank
}
if (rank == 0) {
MPI_Recv from prev_rank
}
值得注意的是,规范 2 只不过是此通用规范的一个特定情况,#ranks
等于 2
。在这种情况下,prev_rank
和 next_rank
都等于 (rank + 1) % 2
,即是一个相同的等级。此外,当排名取 0
或 1
.
(rank + 1) % 2
和 1 - rank
相同
我希望您现在明白背后的动机不是将特定操作硬编码到特定级别,而是使用本地算术来确定要做什么。在您的情况下,每个偶数 ping 消息值都按等级 0 递增,每个奇数 ping 消息值按等级 1 递增,但是如果您将它扩展到一个等级环怎么办? if (rank == ping_value % #ranks) ping_value++;
有点做正确的事并且适用于任何数量的等级。