C++ 交叉MPI Isend/Recv的安全保证
在一次实验中,我了解到执行C++ 交叉MPI Isend/Recv的安全保证,c++,thread-safety,mpi,communication,openmpi,C++,Thread Safety,Mpi,Communication,Openmpi,在一次实验中,我了解到执行request=Isend(…);Recv(…);request.Wait()不保证工作,因为在请求之前,Isend可能不会执行任何操作。Wait(),因此在Recv(…)处死锁(有关详细信息,请参阅原始问题) 但是如果在Recv之外的另一个线程上执行Isend()/Wait(),该怎么办?我现在对标准中的安全保证没有直接的兴趣。这是因为,如果调用适当的Init_thread方法并返回正确的级别,则标准仅断言线程安全。在我的openMPI配置中,情况并非如此。但是,我看
request=Isend(…);Recv(…);request.Wait()
不保证工作,因为在请求之前,Isend
可能不会执行任何操作。Wait()
,因此在Recv(…)
处死锁(有关详细信息,请参阅原始问题)
但是如果在Recv
之外的另一个线程上执行Isend()/Wait()
,该怎么办?我现在对标准中的安全保证没有直接的兴趣。这是因为,如果调用适当的Init_thread
方法并返回正确的级别,则标准仅断言线程安全。在我的openMPI配置中,情况并非如此。但是,我看不出一个原因,即一个实现实际上将调用限制为仅来自调用Init_thread
的线程(需要对线程id进行实际比较)。我的理由是:如果我序列化所有发送和所有RECV,mpi应该永远不会注意到我正在使用多个线程
我的简化代码是:
#include <cassert>
#include <thread>
#include "mpi.h"
void send(int rank, int& item)
{
MPI::Request request = MPI::COMM_WORLD.Isend(&item, sizeof(int), MPI::BYTE, rank, 0);
request.Wait();
}
void recv(int rank, int& item)
{
MPI::COMM_WORLD.Recv(&item, sizeof(int), MPI::BYTE, rank, 0);
}
int main()
{
MPI::Init();
int ns[] = {-1, -1};
int rank = MPI::COMM_WORLD.Get_rank();
ns[rank] = rank;
auto t_0 = std::thread(send, 1 - rank, std::ref(ns[rank])); // send rank to partner (i.e. 1 - rank)
auto t_1 = std::thread(recv, 1 - rank, std::ref(ns[1 - rank])); // receive partner rank from partner
t_0.join();
t_1.join();
assert( ns[0] == 0 );
assert( ns[1] == 1 );
MPI::Finalize();
}
#包括
#包括
#包括“mpi.h”
无效发送(整数排名、整数和项目)
{
MPI::Request Request=MPI::COMM_WORLD.Isend(&item,sizeof(int),MPI::BYTE,rank,0);
request.Wait();
}
无效记录(整数秩、整数和项目)
{
MPI::COMM_WORLD.Recv(&item,sizeof(int),MPI::BYTE,rank,0);
}
int main()
{
MPI::Init();
int ns[]={-1,-1};
int rank=MPI::COMM_WORLD.Get_rank();
ns[秩]=秩;
auto t_0=std::thread(send,1-rank,std::ref(ns[rank]);//将秩发送给伙伴(即1-rank)
auto t_1=std::thread(recv,1-rank,std::ref(ns[1-rank]);//从合作伙伴接收合作伙伴级别
t_0.join();
t_1.连接();
断言(ns[0]==0);
断言(ns[1]==1);
MPI::Finalize();
}
代码说明:每个处理器上执行两个线程。一个尝试向合作伙伴发送一些数据,并等待完成,另一个则从合作伙伴接收一些数据
问题:我可以安全地假设大多数MPI实现不会被这段代码阻塞吗
(免责声明:这段代码不是为了例外安全或特别漂亮而设计的。它仅用于演示目的)
问题:我可以安全地假设大多数MPI实现不会被这段代码阻塞吗
实际上-是的,如果您添加了同步(您的代码缺少同步);理论上-否。虽然有些实现可能允许在MPI\u THREAD\u SINGLE
级别上从不同线程进行序列化调用(Open MPI就是这样一种调用-请参阅),但MPI标准要求库必须在MPI\u THREAD\u SERIALIZED
级别进行初始化。如果您希望您的软件是可移植的,并且能够使用其他MPI实现正确编译和运行,那么您不应该依赖某些特定的开放MPI行为
也就是说,可以将OpenMPI配置为在构建库时支持多线程(MPI\u THREAD\u MULTIPLE
)。默认情况下,出于性能原因,不会启用MT支持。您可以使用ompi\u info
检查特定安装的状态:
$ompi_info | grep MPI_线程|多个
线程支持:poxis(MPI\u线程\u倍数:否,进度:否)
^^^^^^^^^^^^^^^^^^^^^^^
该特定构建不支持多线程,并且在
MPI\u Init\u THREAD
的提供的输出参数中总是返回MPI\u THREAD\u SINGLE
,您在相关问题中了解到的内容是错误的。@hristoilev感谢您在那里发布答案:)这是一个非常好的消息(嗯,部分原因是。这意味着在其他地方有一个bug-。),因为我不必完全抛弃我的概念。我可以问一下代码的哪些部分需要同步?用相同的互斥锁包装Isend
和Recv
就足够了吗?所有MPI调用都必须与关键部分同步,无论它是如何实现的。谢谢,我想我现在看到了解决问题的方法:)请注意,您的问题存在一个单行解决方案:MPI\u Allgather(MPI\u IN\u PLACE,0,MPI\u DATATYPE\u NULL,ns,1,MPI\u INT,MPI\u COMM\u WORLD)代码>(无论C++等效)。这是一个相当简化的示例代码。在我的实际应用程序中,我需要收集主节点上的结果(相当随机)并将作业分发给节点。我可以请你看一下下面的代码并告诉我它是否安全吗?