MPI - 等同于 MPI_SENDRCV 的异步函数
MPI - Equivalent of MPI_SENDRCV with asynchronous functions
我知道 MPI_SENDRECV
可以克服死锁问题(当我们使用经典的 MPI_SEND
和 MPI_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_ISEND
和 MPI_RECV
函数?
据我所知,MPI_ISEND
和 MPI_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_ISEND
、MPI_IRECV
和 MPI_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 有机会完成时完成。实际完成时间有很多同样有效的选项:
- 当您调用阻塞完成函数(如
MPI_WAIT
或 MPI_WAITALL
)时,它们可能会在稍后完成。这些函数保证当阻塞完成调用完成时,您作为参数传入的所有请求都已完成(在您的例子中,MPI_ISEND
和 MPI_IRECV
)。无论操作何时实际发生(请参阅接下来的几个项目符号),您作为应用程序都不能认为它们已完成,直到它们被 MPI_WAIT
或 MPI_TEST
.[= 之类的函数实际标记为已完成。 44=]
操作可以在另一个 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_ISEND
和 MPI_IRECV
实际上可能会在 MPI_BARRIER
期间在后台进行数据传输。这是因为作为应用程序,您在 MPI_BARRIER
调用期间将应用程序的 "control" 传输到 MPI 库。这让库可以在任何正在进行的 MPI 操作上取得进展。最有可能的是,当 MPI_BARRIER
完成时,其他最先完成的事情也完成了。
一些 MPI 库允许您指定您想要 "progress thread"。这告诉 MPI 库在后台启动另一个线程(不是那个线程!=进程),当您的应用程序在主线程中继续时,该线程实际上会为您执行 MPI 操作。
请记住,所有这些最终都需要您实际调用 MPI_WAIT
或 MPI_TEST
或其他类似的函数来确保您的操作真正完成,但是 none当你调用你的非阻塞函数时,这些会产生新的线程或进程来为你完成工作。那些真的就像你把它们放在要做的事情列表上一样(实际上,这是大多数 MPI 库实现它们的方式)。
思考如何实现 MPI_SENDRECV
的最佳方式是使用一个完成函数进行两次非阻塞调用:
MPI_Isend(..., MPI_COMM_WORLD, &req[0]);
MPI_Irecv(..., MPI_COMM_WORLD, &req[1]);
MPI_Waitall(2, req);
我知道 MPI_SENDRECV
可以克服死锁问题(当我们使用经典的 MPI_SEND
和 MPI_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_ISEND
和 MPI_RECV
函数?
据我所知,MPI_ISEND
和 MPI_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_ISEND
、MPI_IRECV
和 MPI_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 有机会完成时完成。实际完成时间有很多同样有效的选项:
- 当您调用阻塞完成函数(如
MPI_WAIT
或MPI_WAITALL
)时,它们可能会在稍后完成。这些函数保证当阻塞完成调用完成时,您作为参数传入的所有请求都已完成(在您的例子中,MPI_ISEND
和MPI_IRECV
)。无论操作何时实际发生(请参阅接下来的几个项目符号),您作为应用程序都不能认为它们已完成,直到它们被MPI_WAIT
或MPI_TEST
.[= 之类的函数实际标记为已完成。 44=] 操作可以在另一个 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_ISEND
和MPI_IRECV
实际上可能会在MPI_BARRIER
期间在后台进行数据传输。这是因为作为应用程序,您在MPI_BARRIER
调用期间将应用程序的 "control" 传输到 MPI 库。这让库可以在任何正在进行的 MPI 操作上取得进展。最有可能的是,当MPI_BARRIER
完成时,其他最先完成的事情也完成了。一些 MPI 库允许您指定您想要 "progress thread"。这告诉 MPI 库在后台启动另一个线程(不是那个线程!=进程),当您的应用程序在主线程中继续时,该线程实际上会为您执行 MPI 操作。
请记住,所有这些最终都需要您实际调用 MPI_WAIT
或 MPI_TEST
或其他类似的函数来确保您的操作真正完成,但是 none当你调用你的非阻塞函数时,这些会产生新的线程或进程来为你完成工作。那些真的就像你把它们放在要做的事情列表上一样(实际上,这是大多数 MPI 库实现它们的方式)。
思考如何实现 MPI_SENDRECV
的最佳方式是使用一个完成函数进行两次非阻塞调用:
MPI_Isend(..., MPI_COMM_WORLD, &req[0]);
MPI_Irecv(..., MPI_COMM_WORLD, &req[1]);
MPI_Waitall(2, req);