MPI_Isend
和MPI_Irecv
存在问题。我正在研究图的邻接矩阵,它是按行分布的。我们可以假设每个处理器包含一行。对于每对索引(i,j)
,我需要发送和接收2个整数。基本上,我需要从其他行接收一些其他信息才能进行计算。我是MPI的新手,在这里进入无限循环,我不确定它是在for循环中使用MPI_Isend
还是MPI_Irecv
的正确方法,也是放置等待的地方。
作为一个例子,假设我们有一个包含6个顶点的图形,因此邻接矩阵(adjMatrix
)将是一个6 * 6矩阵,我们还有一个6 * 2矩阵用于其他一些信息最后,我们在6个处理器之间分配数据。因此:
|0 20 16 0 6 0 | |0 1|
|20 0 0 19 0 6 | |1 1|
addMatrix=|16 0 0 0 12 0 | M=|2 1|
|0 19 0 0 0 12| |3 1|
|6 0 12 0 0 9 | |0 0|
|0 6 0 12 9 0 | |1 0|
我们按如下方式分发矩阵:
P0: |0 20 16 0 6 0 | |0 1|
P1: |20 0 0 19 0 6 | |1 1|
P2: |16 0 0 0 12 0 | |2 1|
P3: |0 19 0 0 0 12| |3 1|
P4: |6 0 12 0 0 9 | |0 0|
P5: |0 6 0 12 9 0 | |1 0|
现在,每个处理器都需要更新adjMatrix
的部分。为此,他们需要来自矩阵M
的某些部分的信息,该部分位于其他处理器中。例如,为了P0
更新(0,1)
的索引20
,它需要有权访问1
矩阵M
的行{1,1}
1}}。因此:
P1
应将MLocal[0][0]=1
和MLocal[0][1]=1
发送到P0
P0
分别以M_j0
和M_j1
收到它们。和
P0
应将MLocal[0][0]=0
和MLocal[0][1]=1
发送到P1
P1
分别以M_j0
和M_j1
收到它们。
for(int i=0;i<rows;i++){
for (int j=0; j<n; j++)
{
int M_j0,M_j1;
MPI_Isend(&MLocal[i][0], 1, MPI_INT, j, my_rank+i*n+j+0, MPI_COMM_WORLD, &send_request0);
MPI_Isend(&MLocal[i][1], 1, MPI_INT, j, my_rank+i*n+j+1, MPI_COMM_WORLD, &send_request1);
MPI_Irecv(&M_j0, 1, MPI_INT, j, my_rank+i*n+j+0, MPI_COMM_WORLD, &recv_request0);
MPI_Irecv(&M_j1, 1, MPI_INT, j, my_rank+i*n+j+1, MPI_COMM_WORLD, &recv_request1);
//MPI_Wait(&send_request0, &status);
//MPI_Wait(&send_request1, &status);
MPI_Wait(&recv_request0, &status);
MPI_Wait(&recv_request1, &status);
// Do something ...
}
}
然后根据GillesGouaillardet的建议,我将4 MPI_Isend
和MPI_Irecv
更改为:
MPI_Sendrecv(&MoatsLocal[i][0], 1, MPI_INT, j, my_rank+i*n+j+0, &M_j0,1, MPI_INT, my_rank, my_rank+i*n+j+0, MPI_COMM_WORLD, &status);
MPI_Sendrecv(&MoatsLocal[i][1], 1, MPI_INT, j, my_rank+i*n+j+1, &M_j1,1, MPI_INT, my_rank, my_rank+i*n+j+1, MPI_COMM_WORLD, &status);
但是,它仍然进入无限循环。
更新:
我更新了代码,问题的某些部分是因为处理器排名和匹配标签。我修复了那个部分,但仍然存在死锁,我想我知道问题出在哪里。也许无法解决它。如果我有足够数量的处理器,将每一行分配给处理器,即n = p,则不会出现任何问题。但问题是处理器的数量小于n
,那么通过主对角线的流量不是很好我通过示例解释它,让我们假设我们有4个处理器和n=6
。这里假设是分布:
P0: |0 20 16 0 6 0 | |0 1|
P1: |20 0 0 19 0 6 | |1 1|
|16 0 0 0 12 0 | |2 1|
P2: |0 19 0 0 0 12| |3 1|
P3: |6 0 12 0 0 9 | |0 0|
|0 6 0 12 9 0 | |1 0|
这就是在循环中徘徊。
第一次迭代:
P0发送和接收来自P1信息的(0,1):&#34; 20&#34;并等待(完成)
P1发送和接收(1,0)的P0信息:&#34; 20&#34;并等待(完成)
P2发送和接收来自P1信息的(3,1):&#34; 19&#34;并等待
P3向/从P0信息发送和接收(4,1):&#34; 6&#34;并等待
第二次迭代:
P0发送和接收来自P1信息的(0,2):&#34; 16&#34;并等待
P1向/从P2信息发送和接收(1,3):&#34; 19&#34;并等待(完成)
对于P1(3,1),P2正在萎缩:&#34; 19&#34;然后就收到了它并完成了!P3等待P0为(4,1):&#34; 6&#34;并等待
第三次迭代:
P0正在等待P1为(0,2):&#34; 16&#34;
P1发送和接收(1,5)的P3信息:&#34; 19&#34;并等待
P2向/从P3信息发送和接收(3,5):&#34; 12&#34;并等待
P3等待P0为(4,1):&#34; 6&#34;
Forth迭代:
P0正在等待P1为(0,2):&#34; 16&#34;
P1等待P3为(1,5):&#34; 19&#34;
P2等待P3为(3,5):&#34; 12&#34;
P3等待P0为(4,1):&#34; 6&#34;
现在,所有人都在等待,我认为没有办法解决它。 ptb建议的解决方案可能有用,我会尝试一下。
仍然,任何其他想法表示赞赏!
答案 0 :(得分:1)
您发布的代码存在一些问题
rows
。但是在您的描述中,行是在处理器之间分配的,因此这可能是一个错误。j=0
时的情况,MPI_Isend(...,j,...)表示每个等级都会向根进程发送一些内容。接下来是对MPI_IRecv(...,j,...),MPI_Wait的调用,这意味着每个进程都将等待从未进入的根进程发送。挑战在于您需要发送和接收来电匹配。一种方法(不一定是最高性能)将通过MPI_Isend在循环中发布所有发送,然后使用MPI_Probe,MPI_Recv处理每个排名recvs(因为recvs的数量是您确切知道的发送数量许多)。伪代码示例:
int send_count = 0;
for (int j=0; j<n; j++) {
if (matrix_entry[j] != 0) {
call MPI_Isend(M_local, 2, MPI_INT, j, 0, ...)
send_count++;
}
}
while (send_count) {
MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, comm, status)
/* get source from status and then call recv */
MPI_Recv(M_j01, 2, MPI_INTEGER, status(MPI_SOURCE), ...)
/* Do something with M_j01 */
send_count--;
}
答案 1 :(得分:1)
一些小建议:
你总是要记住,每个进程都是独立的。进程之间没有同步(如果你放了一个MPI_Barrier就可以。)
我真的不明白你的循环行(行= 6?)
然后所有进程都执行代码.... 这意味着: P0,1,2,3,4,5,6打电话给你的sendrecv,他们都做了6次,因为那些电话是在一个循环...
最后:矩阵的通常大小是多少? 发送很多非常小的消息是一个非常糟糕的主意。
您应该按如下方式设计算法: 1)确定进程PX需要更新其所有列的数据。 2)执行为所有进程收集该数据的通信 3)执行更新。