当我尝试发送2D int数组时,为什么MPI_Send会阻塞?

时间:2017-09-17 16:12:00

标签: mpi openmpi

我正在尝试用mpi执行分形图并行计算。 我将我的计划分为4部分:

  1. 根据每个等级平衡行处理次数
  2. 对每个行属性执行calcul到等级
  3. 将行数和行数发送到等级0
  4. 处理等级0中的数据(对于测试只打印int)
  5. 步骤1和2正在工作,但是当我尝试将行发送到0级时,程序正在停止并阻止。我知道MPI_Send可以阻止在这里没有理由。

    以下是第一步:

    第1步:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /* Include the MPI library for function calls */
    #include <mpi.h>
    
    /* Define tags for each MPI_Send()/MPI_Recv() pair so distinct messages can be
     * sent */
    #define OTHER_N_ROWS_TAG 0
    #define OTHER_PIXELS_TAG 1
    
    int main(int argc, char **argv) {
      const int nRows = 513;
      const int nCols = 513;
      const int middleRow = 0.5 * (nRows - 1);
      const int middleCol = 0.5 * (nCols - 1);
      const double step = 0.00625;
      const int depth = 100;
      int pixels[nRows][nCols];
      int row;
      int col;
      double xCoord;
      double yCoord;
      int i;
      double x;
      double y;
      double tmp;
      int myRank;
      int nRanks;
      int evenSplit;
      int nRanksWith1Extra;
      int myRow0;
      int myNRows;
      int rank;
      int otherNRows;
      int otherPixels[nRows][nCols];
    
      /* Each rank sets up MPI */
      MPI_Init(&argc, &argv);
    
      /* Each rank determines its ID and the total number of ranks */
      MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
      MPI_Comm_size(MPI_COMM_WORLD, &nRanks);
      printf("My rank is %d \n",myRank);
      evenSplit = nRows / nRanks;
      nRanksWith1Extra = nRows % nRanks;
    
    /*Each rank determine the number of rows that he will have to perform (well balanced)*/
      if (myRank < nRanksWith1Extra) {
    
        myNRows = evenSplit + 1;
        myRow0 = myRank * (evenSplit + 1);
      }
      else {
        myNRows = evenSplit;
        myRow0 = (nRanksWith1Extra * (evenSplit + 1)) +
          ((myRank - nRanksWith1Extra) * evenSplit);
      }
    /*__________________________________________________________________________________*/
    

    第2步:

    /*_____________________PERFORM CALCUL ON EACH PIXEL________________________________ */
      for (row = myRow0; row < myRow0 + myNRows; row++) {
    
        /* Each rank loops over the columns in the given row */
        for (col = 0; col < nCols; col++) {
    
          /* Each rank sets the (x,y) coordinate for the pixel in the given row and 
           * column */
          xCoord = (col - middleCol) * step;
          yCoord = (row - middleRow) * step;
    
          /* Each rank calculates the number of iterations for the pixel in the
           * given row and column */
          i = 0;
          x = 0;
          y = 0;
          while ((x*x + y*y < 4) && (i < depth)) {
            tmp = x*x - y*y + xCoord;
            y = 2*x*y + yCoord;
            x = tmp;
            i++;
          }
    
          /* Each rank stores the number of iterations for the pixel in the given
           * row and column. The initial row is subtracted from the current row
           * so the array starts at 0 */
          pixels[row - myRow0][col] = i;
        }
          //printf("one row performed by %d \n",myRank);
    
      }
          printf("work done by %d \n",myRank);
    /*_________________________________________________________________________________*/
    

    第3步:

    /*__________________________SEND DATA TO RANK 0____________________________________*/
    
      /* Each rank (including Rank 0) sends its number of rows to Rank 0 so Rank 0
       * can tell how many pixels to receive */
      MPI_Send(&myNRows, 1, MPI_INT, 0, OTHER_N_ROWS_TAG, MPI_COMM_WORLD);
      printf("test \n");
      /* Each rank (including Rank 0) sends its pixels array to Rank 0 so Rank 0
       * can print it */
      MPI_Send(&pixels, sizeof(int)*myNRows * nCols, MPI_BYTE, 0, OTHER_PIXELS_TAG,
          MPI_COMM_WORLD);
      printf("enter ranking 0 \n");
    /*_________________________________________________________________________________*/
    

    第4步:

    /*________________________TREAT EACH ROW IN RANK 0_________________________________*/
      /* Only Rank 0 prints so the output is in order */
      if (myRank == 0) {
    
        /* Rank 0 loops over each rank so it can receive that rank's messages */
        for (rank = 0; rank < nRanks; rank++){
    
          /* Rank 0 receives the number of rows from the given rank so it knows how
           * many pixels to receive in the next message */
          MPI_Recv(&otherNRows, 1, MPI_INT, rank, OTHER_N_ROWS_TAG,
          MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    
          /* Rank 0 receives the pixels array from each of the other ranks
           * (including itself) so it can print the number of iterations for each
           * pixel */
          MPI_Recv(&otherPixels, otherNRows * nCols, MPI_INT, rank,
              OTHER_PIXELS_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    
          /* Rank 0 loops over the rows for the given rank */
          for (row = 0; row < otherNRows; row++) {
    
            /* Rank 0 loops over the columns within the given row */
            for (col = 0; col < nCols; col++) {
    
              /* Rank 0 prints the value of the pixel at the given row and column
               * followed by a comma */
              printf("%d,", otherPixels[row][col]);
            }
    
            /* In between rows, Rank 0 prints a newline character */
            printf("\n");
          }
        }
      }
    
      /* All processes clean up the MPI environment */
      MPI_Finalize();
    
      return 0;
    }
    

    我想了解为什么会阻止,你能解释一下吗? 我是MPI的新用户,我想学习的不仅仅是让程序正常运行。

    提前谢谢。

3 个答案:

答案 0 :(得分:1)

当您在发送到等级0本身时使用阻塞发送/接收构造时,可能会导致死锁。

来自MPI 3.0 standard, Section 3.2.4

  

Source = destination是允许的,也就是说,进程可以向自己发送消息。 (但是,使用上述阻塞发送和接收操作这样做是不安全的,   因为这可能会导致僵局。见3.5节。)

可能的解决方案:

  • 在发送/接收等级0本身时使用非阻塞发送/接收构造。有关详细信息,请查看MPI_IsendMPI_IrecvMPI_Wait例程。
  • 消除与等级0本身的通信。由于您的等级为0,因此您已经可以知道必须计算多少像素。

答案 1 :(得分:1)

MPI_Send 定义标准阻止操作

请注意,阻止意味着:

  

在消息数据和信封已安全存储之前,它不会返回,以便发送方可以自由修改发送缓冲区。消息可能会直接复制到匹配的接收缓冲区中,也可能会被复制到临时系统缓冲区中。

尝试使用MPI_SendMPI_Recv向自己发送排名是一个死锁。

您的情况的惯用模式是使用适当的集体沟通操作MPI_GatherMPI_Gatherv

答案 2 :(得分:1)

正如之前的回答中所述,MPI_Send() 可能阻止。

从理论上MPI的角度来看,您的应用程序是不正确的,因为在没有发布接收时可能会出现死锁(等级0 MPI_Send()

从非常务实的角度来看,MPI_Send()通常会在发送消息时立即返回(例如myNRows),但会阻止,直到发布匹配的接收为止发送消息时(例如pixels)。请记住

  • small large 至少取决于MPI库和正在使用的互连
  • MPI角度来看,假设MPI_Send()会立即返回small条消息
  • ,这是不正确的

如果您确实希望确保应用程序无死锁,则只需将MPI_Send()替换为MPI_Ssend()

回到你的问题,这里有几个选项

  • 修改您的应用,以便排名0不与自身通信(所有信息都可用,因此无需通信
  • MPI_Irecv()之前发布MPI_Send(),并将MPI_Recv(source=0)替换为MPI_Wait()
  • 修改了您的应用,因此排名0不是MPI_Send()也不是MPI_Recv(source=0),而是MPI_Sendrecv。这是我推荐的选项,因为您只需对通信模式进行一些小改动(计算模式保持不变),这是更优雅的imho。