在for循环

时间:2018-04-04 04:05:35

标签: c++ matrix mpi distributed-computing distributed-system

MPI_IsendMPI_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]=1MLocal[0][1]=1发送到P0 P0   分别以M_j0M_j1收到它们。

     

     

P0应将MLocal[0][0]=0MLocal[0][1]=1发送到P1 P1   分别以M_j0M_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_IsendMPI_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建议的解决方案可能有用,我会尝试一下。

仍然,任何其他想法表示赞赏!

2 个答案:

答案 0 :(得分:1)

您发布的代码存在一些问题

  1. 每个处理器将遍历rows。但是在您的描述中,行是在处理器之间分配的,因此这可能是一个错误。
  2. 发送和接收目的地和来源是相同的。因此,如果考虑j=0时的情况,MPI_Isend(...,j,...)表示每个等级都会向根进程发送一些内容。接下来是对MPI_IRecv(...,j,...),MPI_Wait的调用,这意味着每个进程都将等待从未进入的根进程发送。
  3. MPI_SendRecv调用具有相同的基本问题
  4. 挑战在于您需要发送和接收来电匹配。一种方法(不一定是最高性能)将通过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)执行更新。