MPI中异步发送的安全保障

时间:2014-02-27 20:11:52

标签: c++ mpi

在我的应用程序中,我使用MPI以主从方式分发作业。将工作交给奴隶,然后收集结果。在此程序的多线程版本中,当所有处理器同时尝试Send(阻塞)时,可能存在死锁,因为没有匹配的Recv。我提出了一个似乎工作的解决方案,但我想要保证(除了再测试一次)。

我的程序的安全性是肯定的,如果保证这个小代码 - 提供符合要求的实现。 (显然这只适用于两个处理器而不是更多):

#include <cassert>
#include "mpi.h"

int main()
{
   MPI::Init();
   int ns[] = {-1, -1};
   int rank = MPI::COMM_WORLD.Get_rank();
   ns[rank] = rank;
   MPI::Request request = MPI::COMM_WORLD.Isend(&rank, sizeof(int), MPI::BYTE, 1 - rank, 0);
   MPI::COMM_WORLD.Recv(&ns[1 - rank], sizeof(int), MPI::BYTE, 1 - rank, 0);
   request.Wait();
   assert( ns[0] == 0 );
   assert( ns[1] == 1 );
   MPI::Finalize();
}

所以我的问题是:IsendRecv的交错,直到我在Wait返回的Request上致电Isend在MPI中定义安全的东西?

(免责声明:这段代码不是设计为异常安全或特别漂亮的。它仅用于演示目的)

2 个答案:

答案 0 :(得分:1)

您的代码非常安全。这可以通过MPI标准§3.7.4中定义的非阻塞操作的语义来保证 - 非阻塞通信的语义:

  

进度完成接收的MPI_WAIT调用最终将终止并在匹配的发送开始时返回,除非发送由另一个接收进行。 特别是,如果匹配的发送是非阻止的,那么即使发送方没有执行任何呼叫来完成发送,接收也应该完成。同样,调用完成发送的MPI_WAIT如果匹配的接收已经开始,最终将返回,除非接收由另一个发送进行饱和,即使没有执行任何呼叫来完成接收。

在该上下文中的阻塞操作相当于启动非阻塞操作,紧接着是等待。

如果标准的单词不够安心,那么Open MPI中MPI_SENDRECV实现的代码部分可能有所帮助:

if (source != MPI_PROC_NULL) { /* post recv */
    rc = MCA_PML_CALL(irecv(recvbuf, recvcount, recvtype,
                            source, recvtag, comm, &req));
    OMPI_ERRHANDLER_CHECK(rc, comm, rc, FUNC_NAME);
}

if (dest != MPI_PROC_NULL) { /* send */
    rc = MCA_PML_CALL(send(sendbuf, sendcount, sendtype, dest,
                           sendtag, MCA_PML_BASE_SEND_STANDARD, comm));
    OMPI_ERRHANDLER_CHECK(rc, comm, rc, FUNC_NAME);
}

if (source != MPI_PROC_NULL) { /* wait for recv */
    rc = ompi_request_wait(&req, status);
} else {
    if (MPI_STATUS_IGNORE != status) {
        *status = ompi_request_empty.req_status;
    }
    rc = MPI_SUCCESS;
}

使用Irecv / Send / Wait(receive)Isend / Recv / Wait(send)并不重要 - 两者在遇到可能的死锁时同样安全。当然,如果交错操作没有正确匹配,就会(并且将会)发生死锁。

唯一能使代码出现不一致的事情是它使用C ++ MPI绑定。这些在MPI-2.2中被弃用,在MPI-3.0中被删除。您应该使用C API。

答案 1 :(得分:-1)

此代码无法保证正常运行。在您调用相应的Isend函数之前,MPI实现可以不做与Wait相关的任何操作。

更好的选择是让SendRecv功能无阻塞。您可以使用IsendIrecv,而不是使用Wait,而是使用WaitallWaitall函数接受一系列MPI请求,并同时等待所有这些请求(同时在每个请求上进行)。

所以你的程序看起来像这样:

#include <cassert>
#include "mpi.h"

int main()
{
   MPI::Init();
   int ns[] = {-1, -1};
   int rank = MPI::COMM_WORLD.Get_rank();
   MPI::Request requests[2];
   ns[rank] = rank;
   requests[0] = MPI::COMM_WORLD.Isend(&rank, sizeof(int), MPI::BYTE, 1 - rank, 0);
   requests[1] = MPI::COMM_WORLD.Irecv(&ns[1 - rank], sizeof(int), MPI::BYTE, 1 - rank, 0);
   MPI::Request::Waitall(2, requests)
   assert( ns[0] == 0 );
   assert( ns[1] == 1 );
   MPI::Finalize();
}