简单的 hello world 非阻塞 MPI

问题描述

我正在使用 this website 练习一个简单的非阻塞“Hello world”程序。

#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,MPI_COMM_WORLD,&request);

      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;

    MPI_Isend(&data,(rank + 1) % size,&request);

    MPI_Finalize();
    return 0;
}

我有几个问题:第一个(rank + 1) % size 对我来说没有意义。我希望这只是 rank+1 而不是 (rank + 1) % size。但是,当我删除 %size 时,代码不会运行。我的第二个歧义是这个特定代码的结果:

#PTP job_id=12493
Rank 3 has received message with data 21848 from rank 2
Hello from rank 3 out of 4
Hello from rank 0 out of 4
Rank 2 has received message with data 22065 from rank 1
Hello from rank 2 out of 4
Rank 1 has received message with data 22043 from rank 0
Hello from rank 1 out of 4

我已将数据定义为等于排名,但它似乎抛出了一些随机的东西。这是为什么?

解决方法

TL;DR 代码中的主要问题(并且可能)导致数据显示随机值的原因是使用 MPI_IrecvMPI_Isend 而没有调用 {{ 1}}(或 MPI_Wait)。

MPI_IrecvMPI_Isend 是非阻塞通信例程,因此需要使用 MPI_Wait(或使用 MPI_Test 来测试请求是否完成)以确保消息完成,发送/接收缓冲区中的数据可以再次安全操作。

让我们假设您使用 MPI_Test 发送一个整数数组而不调用 MPI_Isend;在这种情况下,您不确定何时可以安全地修改(或取消分配)该数组的内存。这同样适用于 MPI_Wait。尽管如此,调用 MPI_Irecv 可确保从那时起一种情况下读/写(或取消分配内存)缓冲区,而不会出现 undefined behavior 或不一致数据的风险。

MPI_Wait 期间,必须读取和发送缓冲区的内容(例如 int 数组);同样,在 MPI_Isend 期间,接收缓冲区的内容必须到达。同时,可以将一些计算与正在进行的过程重叠,但是这种计算不能改变(或读取)发送/接收缓冲区的竞争。然后调用 MPI_Irecv 以确保从那时起,可以安全地读取/修改数据发送/接收,而不会出现任何问题。

在您的代码中,无论您如何调用:

MPI_Wait

之后没有调用 MPI_Irecv(&data,1,MPI_INT,rank - 1,MPI_COMM_WORLD,&request); 。此外,您更改缓冲区的内容 MPI_Wait;。如前所述,这会导致未定义的行为

您可以通过使用 MPI_RecvMPI_Send 或通过调用 data=rankMPI_Irecv 后跟 MPI_Isend 来解决此问题。从语义上讲,调用 MPI_Wait 后调用 MPI_Isend() 或调用 MPI_Wait() 后接 MPI_Recv 与调用 MPI_Wait() 和 {{1} 相同},分别。

我有几个问题:第一个是 (rank + 1) % size 对我来说没有意义。我希望这只是排名 + 1 而不是 (等级 + 1)% 大小。

为了向您解释该表达式背后的原因,让我们考虑一下您的代码,其中包含 4 个进程,等级范围从 0 到 3。

进程 1、2 和 3 将调用:

MPI_Send()
  • 进程 1 期待来自进程 0 的消息;
  • 进程 2 期待来自进程 1 的消息;
  • 进程 3 需要一个消息表单进程 2;

然后所有四个进程都调用(让我们相应地替换公式 (rank + 1) % size):

MPI_Recv()
  • 进程 0 从进程 (0 + 1) % 4 -> 1 发送消息;
  • 进程 1 从进程 (1 + 1) % 4 -> 2 发送消息;
  • 进程 2 从进程 (2 + 1) % 4 -> 3 发送消息;
  • 进程 3 从进程 (3 + 1) % 4 -> 0 发送消息;

所以 MPI_Irecv(&data,&request); 被用作一个技巧,这样当你到达最后一个等级时,它会返回第一个的等级。

所有这些都构建了以下接收/发送消息模式 MPI_Isend(&data,(rank + 1) % size,&request);

您可能已经注意到,(rank + 1) % size0 -> 1 -> 2 -> 3 -> 0. 分别被调用了 3 次和 4 次。即使您没有遇到任何与此相关的问题,但通常情况下,不执行相同数量的 MPI_Recv/MPI_Send 调用可能会导致死锁。如果不使用 MPI_Recv 而只使用 MPI_Send 并过滤掉调用 (rank + 1) % size 的最后一个进程,则可以避免这种情况,如下所示:

rank + 1

这意味着进程 3 不会向进程 0 发送消息,但这没关系,因为进程 0 无论如何都不希望收到任何消息。

运行示例:

MPI_Isend

输出: 使用 4 个进程可能的输出:

if(rank + 1 < size)
  MPI_Isend(&data,(rank + 1) 0,&request);

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...