在 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
更新20
的索引(0,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的建议,我将4MPI_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发送和接收 (0,1):"20"和等待(完成)的 P1 信息。
P1 发送和接收 (1,0):"20"和等待(完成)的 P0 信息
P2 发送和接收 (3,1):"19" 的 P1 信息并等待
P3 发送和接收 (4,1):"6" 的 P0 信息并等待
第二次迭代:
P0 发送和接收 (0,2):"16"的 P1 信息并等待
P1 发送和接收 (1,3) 的 P2 信息:"19"并等待(完成)
P2 正在等待 P1 (3,1):"19"然后只是收到它并完成!
P3 正在等待 (4,1):"6"的 P0 并等待
第三次迭代:
P0 正在等待 (0,2):"16" 的 P1
P1 发送和接收 (1,5):"19"的 P3 信息并等待
P2发送和接收 (3,5):"12"的 P3 信息并等待
P3 正在等待 (4,1):"6" 的 P0
第四次迭代:
P0 正在等待 (0,2):"16" 的 P1
P1 正在等待 P3 的 (1,5):"19">
P2 正在等待 P3 的 (3,5):"12">
P3 正在等待 (4,1):"6" 的 P0
现在,所有人都在互相等待,我认为没有任何办法可以解决它。ptb 建议的解决方案可能有效,我会尝试那个。
不过,任何其他想法都是值得赞赏的!
您发布的代码存在一些问题
- 每个处理器将循环遍历
rows
。但是,在您的描述中,行分布在处理器之间,因此这可能是一个错误。 - 发送和接收目标和源是相同的。 因此,如果您考虑
j=0
的情况,MPI_Isend(...,j,...) 表示每个等级都会向根进程发送一些东西。 接下来是调用 MPI_IRecv(...,j,...),MPI_Wait这意味着每个进程都将等待来自永远不会到来的根进程的发送。 - MPI_SendRecv调用具有相同的基本问题
挑战在于您需要发送和接收呼叫才能匹配。 一种方法(不一定是性能最高的)是通过循环中通过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) 执行更新。
- 用MacOS Mojave编译C++:致命错误:mpi.h:没有这样的文件或目录
- MPI突然停止了对多个核心的操作
- 设置 Visual Studio for MPI: 找不到标识符错误
- 使用 make 编译 MPI,几个命名空间错误,例如"错误:未知类型名称'使用'?
- 如何使用 MPI 的远程内存访问 (RMA) 功能并行化数据聚合?
- 重载 MPI 中的运算符 ()
- MPI:检查是否有任何进程已终止
- 使用 pybind11 共享 MPI 通信器
- 使用 CMake,Microsoft MPI 和 Visual Studio 2017 找不到 mpi.h
- 在具有 MPI 的超立方体中广播
- 通过 mpi 发送 c++ 标准::矢量<bool>
- 使用 MPI 的 C++ 中的并行 for 循环
- 如何将 OpenMP 和 MPI 导入到大型 CLion CMake 项目中?
- 如何通过Boost.MPI发送2d Boost.MultiArray的子阵列?
- HDF5 构建了并行支持,但找不到特定于 mpi 的功能
- MPI 集合通信中的指针分配
- 仅特定内核计数上的 MPI 内存损坏
- MPI Isend and Irecv problems
- boost::mpi在具有相同标记的多个isend/irecv传输上抛出mpi_ERR_TRUNCATE
- boost::MPI 的 irecv() 返回未初始化的状态对象