TL;DR 代码中的主要问题(并且可能)数据显示随机值的原因是使用 MPI_Irecv 和 MPI_Isend 而没有调用 MPI_Wait(或 @ 987654331@).
MPI_Irecv 和MPI_Isend 是非阻塞通信程序,因此需要使用MPI_Wait(或使用MPI_Test 来测试请求是否完成)来确保消息完成,并且发送/接收缓冲区中的数据可以再次安全进行操作。
让我们假设您使用MPI_Isend 发送一个整数数组而不调用MPI_Wait;在这种情况下,您不确定何时可以安全地修改(或释放内存)该数组。这同样适用于MPI_Irecv。尽管如此,调用MPI_Wait 可确保从那时起读取/写入(或释放内存)缓冲区,而不会出现undefined behavior 或不一致数据的风险。
在MPI_Isend 期间,必须读取和发送缓冲区的内容(例如整数数组);同样在MPI_Irecv 期间,接收缓冲区的内容必须到达。同时,可以将一些计算与正在进行的进程重叠,但是这种计算不能改变(或读取)发送/接收缓冲区的竞争。然后调用MPI_Wait 以确保从此后,数据发送/接收可以安全地读取/修改而不会出现任何问题。
在您的代码中,无论您如何调用:
MPI_Irecv(&data, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD,&request);
之后无需致电MPI_Wait。此外,您更改了缓冲区的内容即 data=rank;。如前所述,这可能导致未定义的行为。
您可以改用MPI_Recv 和MPI_Send 或调用MPI_Irecv 和MPI_Isend 后跟MPI_Wait 来解决此问题。从语义上讲,调用MPI_Isend() 后调用MPI_Wait() 或调用MPI_Recv 后调用MPI_Wait() 与分别调用MPI_Send() 和MPI_Recv() 相同。
我有几个问题:第一个是 (rank + 1) % size
对我来说没有意义。我希望这只是 rank+1 而不是
(rank + 1) % 大小。
为了向您解释该表达式背后的原因,让我们考虑一下您的代码有 4 个进程,等级从 0 到 3 不等。
进程 1、2 和 3 将调用:
MPI_Irecv(&data, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD,&request);
- 进程 1 需要来自进程 0 的消息;
- 进程 2 需要来自进程 1 的消息;
- 进程 3 需要来自进程 2 的消息;
然后所有四个进程都调用(让我们相应地替换公式 (rank + 1) % size):
MPI_Isend(&data, 1, MPI_INT, (rank + 1) % size, 0, MPI_COMM_WORLD, &request);
- 进程 0 从进程 (0 + 1) % 4 -> 1 发送消息;
- 进程 1 从进程 (1 + 1) % 4 -> 2 发送消息;
- 进程 2 从进程 (2 + 1) % 4 -> 3 发送消息;
- 进程 3 从进程 (3 + 1) % 4 -> 0 发送消息;
所以(rank + 1) % size 被用作一个技巧,这样当你到达最后一个排名时,它会返回第一个排名。
所有这些都是为了构建以下模式的接收/发送消息0 -> 1 -> 2 -> 3 -> 0.
您可能已经注意到 MPI_Recv 和 MPI_Send 分别被调用了 3 次和 4 次。即使您对此没有遇到任何问题,但通常情况下,不执行相同数量的MPI_Recv/MPI_Send 调用可能会导致死锁。如果不使用 (rank + 1) % size 而只使用 rank + 1 并过滤掉调用 MPI_Isend 的最后一个进程,则可以避免这种情况,如下所示:
if(rank + 1 < size)
MPI_Isend(&data, 1, MPI_INT, (rank + 1) 0, MPI_COMM_WORLD, &request);
这意味着进程 3 不会向进程 0 发送消息,但这没关系,因为进程 0 不希望收到任何消息。
一个运行示例:
#include <iostream>
#include <mpi.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
MPI_Request request;
MPI_Status status;
int size, rank, data;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (rank>0) {
MPI_Irecv(&data, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD,&request);
MPI_Wait(&request, &status);
std::cout << "Rank " << rank << " has received message with data " << data<< " from rank " << rank - 1
<< std::endl;
}
std::cout << "Hello from rank " <<rank << " out of " << size<< std::endl;
data=rank;
if(rank + 1 < size){
MPI_Isend(&data, 1, MPI_INT, (rank + 1) 0, MPI_COMM_WORLD, &request);
}
MPI_Wait(&request, &status);
MPI_Finalize();
return 0;
}
输出:
4个进程一个可能的输出:
Hello from rank 0 out of 4
Rank 1 has received message with data 0 from rank 0
Hello from rank 1 out of 4
Rank 2 has received message with data 1 from rank 1
Hello from rank 2 out of 4
Rank 3 has received message with data 2 from rank 2
Hello from rank 3 out of 4