MPI无法通过MPI_send和MPI_Recv向自己发送数据

MPI无法通过MPI_send和MPI_Recv向自己发送数据,mpi,openmpi,Mpi,Openmpi,我正在尝试实现MPI\u Bcast,我计划通过MPI\u Send和MPI\u Recv来实现这一点,但我似乎无法向自己发送消息 代码如下 void My_MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm) { int comm_rank, comm_size, i; MPI_Comm_rank(comm, &comm_rank); MPI_C

我正在尝试实现
MPI\u Bcast
,我计划通过
MPI\u Send
MPI\u Recv
来实现这一点,但我似乎无法向自己发送消息

代码如下

void My_MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm) {
     int comm_rank, comm_size, i;
     MPI_Comm_rank(comm, &comm_rank);
     MPI_Comm_size(comm, &comm_size);
     if(comm_rank==root){
         for(i = 0; i < comm_size; i++){
                 MPI_Send(buffer, count, datatype, i, 0, comm);
         }
     }
     MPI_Recv(buffer, count, datatype, root, 0, comm, MPI_STATUS_IGNORE);
  }
void My\u MPI\u Bcast(void*buffer,int count,MPI\u数据类型Datatype,int root,MPI\u Comm Comm){
int comm_秩,comm_大小,i;
MPI通信等级(通信和通信等级);
MPI_通信大小(通信和通信大小);
if(comm_rank==根){
对于(i=0;i

有什么建议吗?或者我不应该给自己发送消息,而只是做一个内存拷贝?

我认为你应该把
MPI\u Recv(缓冲区、计数、数据类型、根、0、通信、MPI\u状态\u忽略)
仅用于
rank=root
否则它可能会挂起

我认为您应该放置
MPI\u Recv(缓冲区、计数、数据类型、root、0、comm、MPI\u状态\u忽略)
仅适用于
rank=root
否则它可能会挂起

这是一个错误的程序。您不能依赖于向自己发送阻塞MPI_…因为它可能会阻塞。在缓冲区再次可用之前,MPI不保证MPI_发送返回。在某些情况下,这可能意味着它将阻塞,直到目的地接收到消息为止。在您的程序中,目标可能永远不会调用MPI_Recv,因为它仍在尝试发送


现在在My_MPI_Bcast示例中,根进程已经有了数据。为什么要发送或复制它?

这是一个错误的程序。您不能依赖于向自己发送阻塞MPI_…因为它可能会阻塞。在缓冲区再次可用之前,MPI不保证MPI_发送返回。在某些情况下,这可能意味着它将阻塞,直到目的地接收到消息为止。在您的程序中,目标可能永远不会调用MPI_Recv,因为它仍在尝试发送


现在在My_MPI_Bcast示例中,根进程已经有了数据。为什么需要发送或复制它?

根节点上的MPI\u send/MPI\u Recv块可能是死锁

转换为MPI_Isend可用于解决此问题。但是,可能会出现问题,因为发送缓冲区正在被重用,并且root很可能“早”到达MPI_Recv,然后可能在将该缓冲区传输到其他列组之前更改该缓冲区。这在大型工作中尤其可能。此外,如果从fortran调用此例程,则每次MPI_发送调用时可能会出现缓冲区损坏的问题

MPI_Sendrecv的使用只能用于根进程。这将允许对所有非根列组的MPI_发送在根进程进入专用MPI_Sendrecv之前“完成”(例如,可以安全地更改发送缓冲区)。for循环将简单地以“1”而不是“0”开头,并且MPI_Sendrecv调用将添加到该循环的底部。(为什么是一个更好的问题,因为数据在“缓冲区”中,并且将进入“缓冲区”。)


然而,所有这些都回避了一个问题:你为什么要这么做?如果这是一个简单的“学术练习”,写一个点对点的集体电话,那就这样吧。但是,你的方法充其量是幼稚的。在任何合理实现的MPI中,这种总体策略都会被任何MPI_Bcast算法打败

根节点上的MPI_Send/MPI_Recv块可能是死锁

转换为MPI_Isend可用于解决此问题。但是,可能会出现问题,因为发送缓冲区正在被重用,并且root很可能“早”到达MPI_Recv,然后可能在将该缓冲区传输到其他列组之前更改该缓冲区。这在大型工作中尤其可能。此外,如果从fortran调用此例程,则每次MPI_发送调用时可能会出现缓冲区损坏的问题

MPI_Sendrecv的使用只能用于根进程。这将允许对所有非根列组的MPI_发送在根进程进入专用MPI_Sendrecv之前“完成”(例如,可以安全地更改发送缓冲区)。for循环将简单地以“1”而不是“0”开头,并且MPI_Sendrecv调用将添加到该循环的底部。(为什么是一个更好的问题,因为数据在“缓冲区”中,并且将进入“缓冲区”。)


然而,所有这些都回避了一个问题:你为什么要这么做?如果这是一个简单的“学术练习”,写一个点对点的集体电话,那就这样吧。但是,你的方法充其量是幼稚的。在任何合理实现的MPI中,这种总体策略都会被任何MPI_Bcast算法打败

您的程序在多个级别上都有错误。首先,条件中有一个错误:

if(comm_rank=root){
这不会将
comm_rank
root
进行比较,而是将
root
分配给
comm_rank
,然后循环将仅在
root
为非零时执行,并且它将由所有列执行

其次,根进程不需要向自身发送数据,因为数据已经存在。即使您希望发送和接收数据,您也应该注意到
MPI_send
MPI_Recv
都使用相同的缓冲区空间,这是不正确的。一些MPI实现使用直接内存拷贝进行自我交互,即库可能使用
memcpy()
来传输消息。使用带有重叠缓冲区的
memcpy()
会导致未定义的行为

实现线性广播的正确方法是:

void My\u MPI\u Bcast(void*buffer,int count,MPI\u数据类型Datatype,int root,MPI\u Comm Comm)
{
int comm_秩,comm_大小,i;
MPI通信等级(通信和通信等级);
MPI_通信大小(通信和通信大小);
if(comm_rank==根)
{
对于(i=0;i