C MPI发送/接收程序从未完成
我只是花了一段时间写了一个很长的回答别人的问题,只是为了它被删除之前,我可以张贴的答案。我不想白费力气,所以我把问题和答案贴在这里 这不仅仅是一个关于发送/接收死锁的标准答案,因为我还发现了一个只在某些编译器上工作的有趣的半解决方案 在并行课程中,我们需要做一个基于主从设计模式的练习,其中主进程0向其所有从进程发送一条消息,该从进程将向其左右邻居重新发送该消息(处理器id+/-1,除了处理器0没有左邻居和最后一个处理器id没有右邻居之外)。将消息重新传递给邻居后,从处理器向主处理器发送作业结束确认 这个练习很简单,但我的代码中有一个问题,因为我在程序开始时收到确认结束消息…我不明白这里有什么问题。我尝试了使用fflush,但实际上程序的最后一行应该在接收之后才写入控制台 有人知道吗?我对MPI/C概念还不熟悉,所以我的工作可能有问题C MPI发送/接收程序从未完成,c,mpi,deadlock,C,Mpi,Deadlock,我只是花了一段时间写了一个很长的回答别人的问题,只是为了它被删除之前,我可以张贴的答案。我不想白费力气,所以我把问题和答案贴在这里 这不仅仅是一个关于发送/接收死锁的标准答案,因为我还发现了一个只在某些编译器上工作的有趣的半解决方案 在并行课程中,我们需要做一个基于主从设计模式的练习,其中主进程0向其所有从进程发送一条消息,该从进程将向其左右邻居重新发送该消息(处理器id+/-1,除了处理器0没有左邻居和最后一个处理器id没有右邻居之外)。将消息重新传递给邻居后,从处理器向主处理器发送作业结束确
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>
int main(int argc, char *argv[]){
int np, myId;
char send[100], recv[100];
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &np);
MPI_Comm_rank(MPI_COMM_WORLD, &myId);
MPI_Status stat;
if(myId == 0){
int t = sprintf(send, "hey!"); //MPI_get_processor_name
for(int i = 1; i < np; i++){
printf("send %d => %d\n", myId, i);
fflush(stdout);
MPI_Send(send, 50, MPI_CHAR, i, 0, MPI_COMM_WORLD);
}
for(int i = 1; i < np; i++){
MPI_Recv(recv, 50, MPI_CHAR, i, 0, MPI_COMM_WORLD, &stat);
printf("%s\n", recv);
fflush(stdout);
}
}else{
if(myId < (np - 1)){
printf("send %d => %d\n", myId, myId + 1);
fflush(stdout);
MPI_Send(send, 50, MPI_CHAR, myId + 1, 0, MPI_COMM_WORLD);
}
if(myId > 1){
printf("Envoie %d => %d\n", myId, myId - 1);
fflush(stdout);
MPI_Send(send, 50, MPI_CHAR, myId - 1, 0, MPI_COMM_WORLD);
}
MPI_Recv(send, 50, MPI_CHAR, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &stat);
printf("Réception %d <= %d\n", myId, 0);
fflush(stdout);
if(myId != (np - 1)){
MPI_Recv(send, 50, MPI_CHAR, myId + 1, 0, MPI_COMM_WORLD, &stat);
printf("Receive %d <= %d\n", myId, myId + 1);
fflush(stdout);
}
if(myId != 1){
MPI_Recv(send, 50, MPI_CHAR, myId - 1, 0, MPI_COMM_WORLD, &stat);
printf("Receive %d <= %d\n", myId, myId - 1);
fflush(stdout);
}
int t = sprintf(recv, "End for %d.", myId);
MPI_Send(recv, 50 , MPI_CHAR, 0, 0, MPI_COMM_WORLD);
}
MPI_Finalize();
return 0;
}
#包括
#包括
#包括
#包括
int main(int argc,char*argv[]){
int-np,myId;
char send[100],recv[100];
MPI_Init(&argc,&argv);
MPI通信大小(MPI通信世界和np);
MPI通信等级(MPI通信世界和myId);
MPI_状态统计;
如果(myId==0){
int t=sprintf(发送,“嘿!”);//MPI\u获取\u处理器\u名称
对于(int i=1;i%d\n”,myId,i);
fflush(stdout);
MPI_发送(发送,50,MPI_字符,i,0,MPI_通信世界);
}
对于(int i=1;i%d\n”,myId,myId+1);
fflush(stdout);
MPI_发送(发送,50,MPI_字符,myId+1,0,MPI_通信世界);
}
如果(myId>1){
printf(“环境%d=>%d\n”,myId,myId-1);
fflush(stdout);
MPI_发送(发送,50,MPI_字符,myId-1,0,MPI_通信世界);
}
MPI_Recv(发送、50、MPI_字符、MPI_任意源、0、MPI_通信世界和统计);
printf(“Ré异常%d解决方案1
让我们比较一下所有非0“从”内核实际上在做什么,以及您所说的它们应该做什么
您希望他们做什么:
主进程0向其所有从进程发送一条消息,该从进程将向其左右邻居(处理器id+/-1,处理器0没有左邻居和最后一个处理器id没有右邻居除外)重新发送消息。将消息重新传递给邻居后,从处理器向主处理器发送作业结束的确认
代码大纲:
Send_To_Right_Neighbour();
Send_To_Left_Neighbour();
Receive_From_Master();
Receive_From_Right_Neighbour();
Receive_From_Left_Neighbour();
Send_To_Master();
看到区别了吗?在将消息重新发送给邻居之前,从机没有收到来自主机的消息。将代码更改为:
Receive_From_Master();
Send_To_Right_Neighbour();
Send_To_Left_Neighbour();
Receive_From_Right_Neighbour();
Receive_From_Left_Neighbour();
Send_To_Master();
将修复该问题,然后代码为我运行到完成
出什么事了
MPI\u Send
可以是一个阻塞函数——也就是说,在另一个进程调用了匹配的MPI\u Recv
之前,对MPI\u Send
的调用不会返回(尽管它不一定是阻塞函数)。您应该假设它在编写代码时总是阻塞的
现在,让我们想象一下,当运行>5个进程时,非0进程会做什么
- 进程1发送到它的右邻居(进程2),并在那里等待,直到进程2调用MPI_Recv
MPI\u Recv
MPI\u Send
和MPI\u Recv
调用组合到MPI\u Sendrecv
c中alls。这将在同一步骤中发送和接收到邻居,从而消除死锁问题。以前,从0接收的调用介于发送和接收到同一邻居之间,因此编译器无法将其优化为单个操作
但是我们不想依赖于有一个好的编译器——您的代码应该在任何符合标准的编译器上工作——因此我们应该自己手动修复死锁问题,而不是依赖于编译器的聪明
解决方案2
首先,对课程中迄今为止可能涉及或未涉及的内容发表一些评论
- 进程0正在向所有其他内核发送相同的信息。如果您知道
,则应使用该信息,而不是所有这些发送和接收信息。MPI\u Bcast
Receive_From_Master(); // Make sure all info is sent from left to right Send_To_Right_Neighbour(); // Make sure any info is received from left to right Receive_From_Left_Neighbour(); // Now send all info from right to left Send_To_Left_Neighbour(); // Make sure any info is received Receive_From_Right_Neighbour(); Send_To_Master();