MPI中异步发送的安全保证
Safety guarantee for asynchronous sends in 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();
}
所以我的问题是:Isend
和Recv
的交错,直到我在Isend
返回的Request
上调用Wait
,在MPI中是一件定义明确的安全事情吗?
(免责声明:这段代码不是为了异常安全或特别漂亮而设计的。它仅用于演示目的)
您的代码是完全安全的。MPI标准§3.7.4-非阻塞通信的语义:中定义的非阻塞操作的语义保证了这一点
Progress如果启动了匹配的发送,则完成接收的对
MPI_WAIT
的调用最终将终止并返回,除非另一个接收满足了发送特别是,如果匹配的发送是非阻塞的,那么即使发送方没有执行任何调用来完成发送,接收也应该完成类似地,如果启动了匹配的接收,则完成发送的对MPI_WAIT
的调用最终将返回,除非接收被另一个发送所满足,即使没有执行任何调用来完成接收。
在该上下文中的阻塞操作相当于启动一个非阻塞操作,然后立即等待。
如果标准中的文字不够令人放心,那么这段来自OpenMPI中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。
此代码不能保证工作。MPI实现可以自由地不做任何与Isend
相关的事情,直到调用相应的Wait
函数。
更好的选择是使Send
和Recv
函数都不阻塞。您将使用Isend
和Irecv
,而不是使用Wait
,而是使用Waitall
。Waitall
函数接收一组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();
}
- 从不同线程使用int64的不同字节安全吗
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 虚拟决赛作为安全
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何将元素添加到数组的线程安全函数?
- C++中的线程安全删除
- 通过网络、跨平台传递std::变体是否安全
- 在std::thread中,joinable()然后join()线程安全吗
- 使用std::istream::peek()总是安全的吗
- 从值小于256的uint16到uint8的Endian安全转换
- 窗口上信号处理程序的异步安全写入函数
- Boost 堆栈跟踪异步信号安全吗?
- 异步记录器线程安全
- MPI中异步发送的安全保证
- 信号处理程序异步安全函数
- 对矢量(vector::operator[]和vector:(size))的只读访问是异步安全的
- Boost:在异步调用中使用多个递归是否安全
- 如何使一个函数异步信号安全
- c++中的异步线程安全日志记录(无互斥)
- 是更好的同步或异步从boost asio时,有大量的计算和推/弹出线程安全容器