MPI - 等同于 MPI_SENDRCV 的异步函数

MPI - Equivalent of MPI_SENDRCV with asynchronous functions

我知道 MPI_SENDRECV 可以克服死锁问题(当我们使用经典的 MPI_SENDMPI_RECV 函数时)。

我想知道 MPI_SENDRECV(sent_to_process_1, receive_from_process_0) 是否等同于:

MPI_ISEND(sent_to_process_1, request1)
MPI_IRECV(receive_from_process_0, request2)
MPI_WAIT(request1)
MPI_WAIT(request2)

具有异步 MPI_ISENDMPI_RECV 函数?

据我所知,MPI_ISENDMPI_RECV 创建了一个分支(即 2 个进程)。所以如果我按照这个逻辑,MPI_ISEND 的第一次调用会生成 2 个进程。一个进行通信,另一个调用 MPI_RECV,后者分叉 2 个进程。

但是第一个MPI_ISEND的通信结束后,第二个进程会再次调用MPI_IRECV吗?按照这个逻辑,上面的等效项似乎无效...

也许我应该改成这样:

MPI_ISEND(sent_to_process_1, request1)
MPI_WAIT(request1)
MPI_IRECV(receive_from_process_0, request2)
MPI_WAIT(request2)

但我认为这也可能造成死锁。

任何人都可以给我另一个使用 MPI_ISENDMPI_IRECVMPI_WAIT 的解决方案以获得与 MPI_SEND_RECV?

相同的行为

我通常如何在与节点 i+1 通信的节点 i 上执行此操作:

mpi_isend(send_to_process_iPlus1, requests(1))
mpi_irecv(recv_from_process_iPlus1, requests(2))
...
mpi_waitall(2, requests)

您可以看到如何通过非阻塞通信以这种方式排序您的命令允许您(在上面的 ... 期间)执行任何不依赖于 send/recv 缓冲区的计算沟通。重叠计算与通信通常对于最大化性能至关重要。

mpi_send_recv 另一方面(同时避免任何死锁问题)仍然是一个阻塞操作。因此,您的程序必须在整个 send/recv 过程中保持在该例程中。

最后一点:您可以初始化 2 个以上的请求,并使用上述结构以与处理 2 个请求相同的方式等待所有请求。例如,也很容易开始与节点 i-1 的通信并等待所有 4 个请求。使用 mpi_send_recv 您必须始终配对发送和接收;如果您只想发送怎么办?

问题和其他答案中存在一些危险的思路。当您启动非阻塞 MPI 操作时,MPI 库不会创建新的 process/thread/etc。我相信您正在考虑更像是 OpenMP 的并行区域,其中创建了新的 threads/tasks 来完成一些工作。

在 MPI 中,启动非阻塞操作就像告诉 MPI 库您有一些事情希望在 MPI 有机会完成时完成。实际完成时间有很多同样有效的选项:

  1. 当您调用阻塞完成函数(如 MPI_WAITMPI_WAITALL)时,它们可能会在稍后完成。这些函数保证当阻塞完成调用完成时,您作为参数传入的所有请求都已完成(在您的例子中,MPI_ISENDMPI_IRECV)。无论操作何时实际发生(请参阅接下来的几个项目符号),您作为应用程序都不能认为它们已完成,直到它们被 MPI_WAITMPI_TEST.[= 之类的函数实际标记为已完成。 44=]
  2. 操作可以在另一个 MPI 操作期间完成 "in the background"。例如,如果您执行类似以下代码的操作:

    MPI_Isend(..., MPI_COMM_WORLD, &req[0]);
    MPI_Irecv(..., MPI_COMM_WORLD, &req[1]);
    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Waitall(2, req);
    

    MPI_ISENDMPI_IRECV 实际上可能会在 MPI_BARRIER 期间在后台进行数据传输。这是因为作为应用程序,您在 MPI_BARRIER 调用期间将应用程序的 "control" 传输到 MPI 库。这让库可以在任何正在进行的 MPI 操作上取得进展。最有可能的是,当 MPI_BARRIER 完成时,其他最先完成的事情也完成了。

  3. 一些 MPI 库允许您指定您想要 "progress thread"。这告诉 MPI 库在后台启动另一个线程(不是那个线程!=进程),当您的应用程序在主线程中继续时,该线程实际上会为您执行 MPI 操作。

请记住,所有这些最终都需要您实际调用 MPI_WAITMPI_TEST 或其他类似的函数来确保您的操作真正完成,但是 none当你调用你的非阻塞函数时,这些会产生新的线程或进程来为你完成工作。那些真的就像你把它们放在要做的事情列表上一样(实际上,这是大多数 MPI 库实现它们的方式)。

思考如何实现 MPI_SENDRECV 的最佳方式是使用一个完成函数进行两次非阻塞调用:

MPI_Isend(..., MPI_COMM_WORLD, &req[0]);
MPI_Irecv(..., MPI_COMM_WORLD, &req[1]);
MPI_Waitall(2, req);