代码之家  ›  专栏  ›  技术社区  ›  Sarah

在for循环中使用MPI\u Irecv和MPI\u Isend

  •  0
  • Sarah  · 技术社区  · 7 年前

    我对 MPI_Isend MPI_Irecv 。我正在研究图的邻接矩阵,它是按行分布的。我们可以假设每个处理器包含一行。对于每对索引 (i,j) 我需要发送和接收2个整数。基本上,为了进行计算,我需要从其他行接收一些其他信息。我是MPI新手,在这里它进入无限循环,我甚至不确定它是不是正确的使用方式 MPI\u Isend MPI\U Irecv 在for循环中,也是放置等待的位置。

    例如,假设我们有一个有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|
    

    现在,每个处理器都需要更新其 调整矩阵 。为此,他们需要从矩阵的某些部分获得信息 M ,它位于其他处理器中。例如,为了 P0 更新索引 (0,1) 这是 20 ,它需要有权访问该行 1 矩阵的 M 这是 {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\u j0 M\U 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\u Isend MPI\U 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):“20”并等待(完成)

    P1向P0发送和接收信息(1,0):“20”并等待(完成)

    P2向P1发送和接收(3,1)的信息:“19”并等待

    P3向P0发送和接收(4,1)的信息:“6”并等待

    第二次迭代:

    P0向P1发送和接收信息(0,2):“16”并等待

    P1向P2发送和接收(1,3)的信息:“19”并等待(完成)

    P2为P1(3,1)哭泣:“19”然后就收到并完成了!

    P3正在等待P0(4,1):“6”,然后等待

    第三次迭代:

    P0正在等待P1的(0,2):“16”

    P1向P3发送和接收(1,5)的信息:“19”并等待

    P2向P3发送和接收(3,5)的信息:“12”并等待

    P3正在等待(4,1):“6”的P0

    第四次迭代:

    P0正在等待P1的(0,2):“16”

    P1正在等待P3的(1,5):“19”

    P2正在等待P3的(3,5):“12”

    P3正在等待(4,1):“6”的P0

    现在,所有人都在等待对方,我认为没有任何办法解决它。ptb建议的解决方案可能会奏效,我会试试那个。

    尽管如此,任何其他想法都是值得赞赏的!

    2 回复  |  直到 7 年前
        1
  •  1
  •   ptb    7 年前

    您发布的代码有一些问题

    1. 每个处理器将循环通过 rows 。但是,在您的描述中,行分布在处理器之间,因此这可能是一个错误。
    2. 发送和接收目标和源是相同的。所以如果你考虑一下 j=0 ,MPI\u Isend(…,j,…)意味着每个列组都会向根进程发送一些内容。然后调用MPI\U IRecv(…,j,…),MPI\u Wait,这意味着每个进程都将等待根进程的发送,而根进程永远不会发送。
    3. MPI\u SendRecv调用有相同的基本问题

    面临的挑战是,您需要您的发送和接收呼叫进行匹配。实现这一点的一种方法(不一定是性能最好的)是通过MPI\u Isend在一个循环中发布所有发送,然后使用MPI\u Probe、MPI\u Recv处理每个列的Recv(因为Recv的数量是您确切知道的发送数量)。伪代码示例:

    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--;
    } 
    
        2
  •  1
  •   David Daverio    7 年前

    一些小建议:

    您必须始终记住,每个过程都是独立的。。进程之间没有同步(设置MPI\U屏障时除外)。

    我真的不理解您对行的循环(行是否=6?)

    然后所有进程执行代码。。。。 这意味着: P0,1,2,3,4,5,6调用您的sendrecv,所有这些调用都会执行6次,因为这些调用处于循环中。。。

    最后:矩阵的通常大小是多少? 发送大量非常小的消息是一个非常糟糕的主意。

    您应该按照以下方式设计算法: 1) 找出进程PX需要哪些数据来更新其所有列。 2) 执行通信,为所有流程收集此数据 3) 执行更新。

    推荐文章