【问题标题】:Simple hello world non blocking MPI简单的 hello world 非阻塞 MPI
【发布时间】:2021-05-19 14:01:14
【问题描述】:

我正在使用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, 0,  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, 1, MPI_INT, (rank + 1) % size, 0, MPI_COMM_WORLD, &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

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

【问题讨论】:

    标签: c++ performance parallel-processing mpi nonblocking


    【解决方案1】:

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

    MPI_IrecvMPI_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_RecvMPI_Send 或调用MPI_IrecvMPI_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 -&gt; 1 -&gt; 2 -&gt; 3 -&gt; 0.

    您可能已经注意到 MPI_RecvMPI_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
    

    【讨论】:

    • 很好的答案!太棒了!
    • 一个地狱般的答案
    猜你喜欢
    • 2015-08-17
    • 1970-01-01
    • 2012-08-24
    • 2021-12-13
    • 2011-05-25
    • 2011-05-03
    • 2014-04-04
    • 2011-04-23
    • 2018-04-26
    相关资源
    最近更新 更多