一个不安全的 MPI 非阻塞通信示例?

Posted

技术标签:

【中文标题】一个不安全的 MPI 非阻塞通信示例?【英文标题】:A not safe MPI non-blocking communication example? 【发布时间】:2020-08-30 05:48:05 【问题描述】:

我正在我的程序中实现 MPI 非阻塞通信。我在 MPI_Isend man_page 上看到,它说:

非阻塞发送调用表明系统可能开始从发送缓冲区复制数据。在调用非阻塞发送操作后,发送方不应修改发送缓冲区的任何部分,直到发送完成。

我的代码是这样工作的:

// send messages
if(s > 0)

    MPI_Requests s_requests[s];
    MPI_Status   s_status[s];

    for(int i = 0; i < s; ++i)

        // some code to form the message to send
        std::vector<doubel> send_info;

        // non-blocking send
        MPI_Isend(&send_info[0], ..., s_requests[i]);
    

    MPI_Waitall(s, s_requests, s_status);


// recv info
if(n > 0)    // s and n will match

    for(int i = 0; i < n; ++i)

        MPI_Status status;

        // allocate the space to recv info
        std::vector<double> recv_info;

        MPI_Recv(&recv_info[0], ..., status)
    




我的问题是:我是否修改了发送缓冲区,因为它们位于内部大括号中(send_info 向量在循环结束后被杀死)?因此,这不是一种安全的通信方式吗?虽然我的程序现在运行良好,但我仍然被怀疑。感谢你的回复。

【问题讨论】:

这意味着当你在 send 方法中时不要从另一个线程修改它。你没有这样做。 您的缓冲区是/可能在堆栈上分配,因此可以在发送之前被覆盖。在我看来,MPI_Isend() 的用法是错误的。 是的。我使用std::vector,所以它是在堆栈上分配的。那么正确的做法是将MPI_Wait()MPI_Isend()放在同一个循环中? 是的,这是一种选择,但可能相当于阻止 MPI_Send()。其他选项包括在for 循环之前分配一个巨大的缓冲区,并在之后分配一个MPI_Waitall()。另一种常见的技术是使用 2 个缓冲区:isend(buffer0);isend(buffer1);wait(req0);isend(buffer(0);wait(req1); ...,因此在保持内存使用合理的同时,计算和通信之间仍有一些重叠空间。 请注意,您不太可能在短消息中看到任何错误(以 Eager 模式发送),但更有可能在大消息中发送不正确的数据。 【参考方案1】:

在这个例子中我想强调两点。

第一个是我质疑的问题:发送缓冲区在MPI_Waitall 之前被修改。原因就是吉尔斯所说的。并且解决方案可以在for loop 之前分配一个大缓冲区,并在循环完成后使用MPI_Waitall 或将MPI_Wait 放入循环内。但后一种在性能意义上相当于使用MPI_Send

但是,我发现如果你只是简单地转移到阻塞发送和接收,这样的通信方案可能会导致死锁。类似于经典的死锁:

if (rank == 0) 
      MPI_Send(..., 1, tag, MPI_COMM_WORLD);
      MPI_Recv(..., 1, tag, MPI_COMM_WORLD, &status);
  else if (rank == 1) 
      MPI_Send(..., 0, tag, MPI_COMM_WORLD);
      MPI_Recv(..., 0, tag, MPI_COMM_WORLD, &status);
 

可以在here找到解释。

我的程序可能会导致类似的情况:所有处理器都调用MPI_Send,那么它就是死锁。

所以我的解决方案是使用大缓冲区并坚持非阻塞通信方案。

#include <vector>
#include <unordered_map>

// send messages
if(s > 0)

    MPI_Requests s_requests[s];
    MPI_Status   s_status[s];

    std::unordered_map<int, std::vector<double>> send_info;

    for(int i = 0; i < s; ++i)


        // some code to form the message to send
        send_info[i] = std::vector<double> ();

        // non-blocking send
        MPI_Isend(&send_info[i][0], ..., s_requests[i]);
    

    MPI_Waitall(s, s_requests, s_status);


// recv info
if(n > 0)    // s and n will match

    for(int i = 0; i < n; ++i)

        MPI_Status status;

        // allocate the space to recv info
        std::vector<double> recv_info;

        MPI_Recv(&recv_info[0], ..., status)
    


【讨论】:

以上是关于一个不安全的 MPI 非阻塞通信示例?的主要内容,如果未能解决你的问题,请参考以下文章

MPI中的缓冲区和非阻塞通信

结构填充和非阻塞通信缓冲区问题导致的 MPI 派生数据类型问题

MPI 中不同类型的点对点通信在性能方面有何不同?

非阻塞 MPI 和会合协议

连续 MPI 非阻塞调用

mpi4py 点到点通信总结