在 for 循环中使用 MPI_Irecv 和 MPI_Isend
Using MPI_Irecv and MPI_Isend in a for loop
我对 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
,它需要访问矩阵 M
的行 1
即 {1,1}
。因此:
P1
should send MLocal[0][0]=1
and MLocal[0][1]=1
to P0
in which P0
receives them as M_j0
and M_j1
, respectively.
And
P0
should send MLocal[0][0]=0
and MLocal[0][1]=1
to P1
in which P1
receives them as M_j0
and M_j1
, respectively.
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);
但是,它仍然进入了无限循环。
UPDATE:
我更新了代码,部分问题是因为处理器排名和匹配标签。我修复了那部分,但它仍然容易死锁,我想我知道问题出在哪里。而且可能无法解决。如果我有足够数量的处理器,将每一行分配给一个处理器,即 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 send and receive to/from P1 information for (0,1):"20" and wait(done)
P1 send and receive to/from P0 information for (1,0):"20" and wait(done)
P2 send and receive to/from P1 information for (3,1):"19" and wait
P3 send and receive to/from P0 information for (4,1):"6" and wait
第二次迭代:
P0 send and receive to/from P1 information for (0,2):"16" and wait
P1 send and receive to/from P2 information for (1,3):"19" and wait(done)
P2 was wainting for P1 (3,1):"19" then just recieved it and done!
P3 is waiting for P0 for (4,1):"6" and wait
第三次迭代:
P0 is waiting for P1 for (0,2):"16"
P1 send and receive to/from P3 information for (1,5):"19" and wait
P2 send and receive to/from P3 information for (3,5):"12" and wait
P3 is waiting for P0 for (4,1):"6"
第四次迭代:
P0 is waiting for P1 for (0,2):"16"
P1 is waiting for P3 for (1,5):"19"
P2 is waiting for P3 for (3,5):"12"
P3 is waiting for P0 for (4,1):"6"
现在都在互相等待,我觉得没有什么办法可以解决。 ptb 建议的解决方案可能有效,我会尝试那个。
不过,如果有任何其他想法,我们将不胜感激!
您post编辑
的代码存在一些问题
- 每个处理器都会循环
rows
。但是在您的描述中,行分布在处理器之间,因此这可能是一个错误。
- 发送和接收的目的地和来源相同。因此,如果您考虑
j=0
时的情况,MPI_Isend(...,j,...) 意味着每个级别都会向根进程发送一些内容。接下来是对 MPI_IRecv(...,j,...), MPI_Wait 的调用,这意味着每个进程都将等待来自根进程的发送,但它永远不会到来。
- MPI_SendRecv调用有相同的基本问题
挑战在于您需要发送和接收调用匹配。一种方法(不一定是最高性能的)是 post 循环中通过 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--;
}
一些小建议:
你永远要记住每个进程都是独立的。进程之间没有同步(如果你放一个MPI_Barrier)。
我真的不明白你对行的循环(行 = 6 吗?)
然后所有进程执行代码....
这表示:
P0,1,2,3,4,5,6 调用你的 sendrecv,它们都调用了 6 次,因为这些调用在循环中...
最后:矩阵的通常大小是多少?
发送很多非常小的消息是一个非常糟糕的主意。
你应该设计你的算法如下:
1) 找出流程 PX 需要更新其所有列的哪些数据。
2)执行为所有进程收集此数据的通信
3) 执行更新。
我对 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
,它需要访问矩阵 M
的行 1
即 {1,1}
。因此:
P1
should sendMLocal[0][0]=1
andMLocal[0][1]=1
toP0
in whichP0
receives them asM_j0
andM_j1
, respectively.And
P0
should sendMLocal[0][0]=0
andMLocal[0][1]=1
toP1
in whichP1
receives them asM_j0
andM_j1
, respectively.
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);
但是,它仍然进入了无限循环。
UPDATE:
我更新了代码,部分问题是因为处理器排名和匹配标签。我修复了那部分,但它仍然容易死锁,我想我知道问题出在哪里。而且可能无法解决。如果我有足够数量的处理器,将每一行分配给一个处理器,即 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 send and receive to/from P1 information for (0,1):"20" and wait(done)
P1 send and receive to/from P0 information for (1,0):"20" and wait(done)
P2 send and receive to/from P1 information for (3,1):"19" and wait
P3 send and receive to/from P0 information for (4,1):"6" and wait
第二次迭代:
P0 send and receive to/from P1 information for (0,2):"16" and wait
P1 send and receive to/from P2 information for (1,3):"19" and wait(done)
P2 was wainting for P1 (3,1):"19" then just recieved it and done!
P3 is waiting for P0 for (4,1):"6" and wait
第三次迭代:
P0 is waiting for P1 for (0,2):"16"
P1 send and receive to/from P3 information for (1,5):"19" and wait
P2 send and receive to/from P3 information for (3,5):"12" and wait
P3 is waiting for P0 for (4,1):"6"
第四次迭代:
P0 is waiting for P1 for (0,2):"16"
P1 is waiting for P3 for (1,5):"19"
P2 is waiting for P3 for (3,5):"12"
P3 is waiting for P0 for (4,1):"6"
现在都在互相等待,我觉得没有什么办法可以解决。 ptb 建议的解决方案可能有效,我会尝试那个。
不过,如果有任何其他想法,我们将不胜感激!
您post编辑
的代码存在一些问题- 每个处理器都会循环
rows
。但是在您的描述中,行分布在处理器之间,因此这可能是一个错误。 - 发送和接收的目的地和来源相同。因此,如果您考虑
j=0
时的情况,MPI_Isend(...,j,...) 意味着每个级别都会向根进程发送一些内容。接下来是对 MPI_IRecv(...,j,...), MPI_Wait 的调用,这意味着每个进程都将等待来自根进程的发送,但它永远不会到来。 - MPI_SendRecv调用有相同的基本问题
挑战在于您需要发送和接收调用匹配。一种方法(不一定是最高性能的)是 post 循环中通过 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--;
}
一些小建议:
你永远要记住每个进程都是独立的。进程之间没有同步(如果你放一个MPI_Barrier)。
我真的不明白你对行的循环(行 = 6 吗?)
然后所有进程执行代码.... 这表示: P0,1,2,3,4,5,6 调用你的 sendrecv,它们都调用了 6 次,因为这些调用在循环中...
最后:矩阵的通常大小是多少? 发送很多非常小的消息是一个非常糟糕的主意。
你应该设计你的算法如下: 1) 找出流程 PX 需要更新其所有列的哪些数据。 2)执行为所有进程收集此数据的通信 3) 执行更新。