Если операторы между MPI Isend / Irecv и MPI ждут, чтобы предотвратить выполнение программы.Что может быть причиной этого? - PullRequest
0 голосов
/ 22 января 2019

Я создаю программу для расчета потенциала между двумя проводниками с использованием MPI. Я использую неблокирующую отправку и получение, чтобы можно было выполнять вычисления, пока информация передается между процессорами.

Однако операторы if между командами isend и irecv и wait, в которых содержатся вычисления, не вводятся. Когда операторы if и вычисления удалены, программа переходит к оператору ожидания.

Я проверил, что расчеты верны и не вызывают проблем. Я проверил, что условия для утверждений if верны.

Вот раздел кода теста:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <mpi.h>

int main(int argc, char *argv[])
{
  /*MPI Specific Variables*/
  int my_size, my_rank, up, down;
  MPI_Request reqU, reqD, sreqU, sreqD;
  MPI_Status rUstatus, rDstatus, sUstatus, sDstatus;  

  /*Physical Dimensions*/
  double phi_0 = 1000.0;/*V*/

  /*Other Variables*/
  int grid_size = 100;
  int slice = 50;
  int x,y;
  double grid_res_y = 0.2;
  double grid_res_x = 0.1;
  int xboundary = 10;
  int yboundary = 25;
  int boundary_proc = 2;

  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
  MPI_Comm_size(MPI_COMM_WORLD, &my_size);

  /*Determining neighbours*/
  if (my_rank != 0) /*if statemets used to stop highest and lowest rank neighbours arent outside 0 - my_size-1 range of ranks*/
    {
      up = my_rank-1;
    }
  else
    {
      up = MPI_PROC_NULL;
    }

  if(my_rank != my_size-1)
    {
      down = my_rank+1;
    }
  else
   {
      down = MPI_PROC_NULL;
    }

  /*cross-check: presumed my_size is a factor of gridsize else there are odd sized slices and this is not coded for*/
  if (grid_size%my_size != 0)
    {
      printf("ERROR - number of procs =  %d, this is not a factor of grid_size %d\n", my_size, grid_size);
      exit(0);
    }

  /*Set Up Distributed Data Approach*/
  double phi[slice+2][grid_size]; /*extra 2 rows to allow for halo data*/

  for (y=0; y < slice+2; y++)
    {
      for (x=0; x < grid_size; x++)
        { 
          phi[y][x] = 0.0;
        }
    }

  if(my_rank == 0) /*Boundary Containing rank does 2 loops. One over part with inner conductor and one over part without inner conductor*/
    {
      for(y=0; y < slice+1; y++)
        {
          for(x=xboundary; x < grid_size; x++)
            {
              phi[y][x] = phi_0;
            }
        }   
    }


  if (my_rank < my_size-1)
    {
      /*send top most strip up one node to be recieved as bottom halo*/
      MPI_Isend(&phi[1][0], grid_size  , MPI_DOUBLE, down, 1, MPI_COMM_WORLD, &sreqU);  
      /*recv top halo from up one node*/
      MPI_Irecv(&phi[slice+1][0], grid_size, MPI_DOUBLE, down, 2, MPI_COMM_WORLD, &reqU);
    }

  if (my_rank > 0)
    {
      /*recv top halo from down one node*/
      MPI_Irecv(&phi[0][0], grid_size , MPI_DOUBLE, up, 2, MPI_COMM_WORLD, &reqD);
      /*send bottom most strip down one node to be recieved as top halo*/
      MPI_Isend(&phi[slice][0], grid_size , MPI_DOUBLE, up, 1, MPI_COMM_WORLD, &sreqD);
    }

  printf("send/recv complete");

  if (my_rank < boundary_proc)
     {
        printf("rank %d Entered if", my_rank);
        /*Calculations*/
     }

  else if(my_rank > boundary_proc)
    {
        printf("rank %d Entered else if", my_rank);
        /*calculations*/
    }

  else
     {
        printf("rank %d Entered else", my_rank);
        /*calculations*/
     }

  if (my_rank<my_size-1)
   {
     /*Wait for send to down one rank to complete*/
     MPI_Wait(&sreqD, &sDstatus);
     /*Wait for recieve from up one rank to complete*/
     MPI_Wait(&reqD, &rDstatus);
   }

  if (my_rank>0)
   {
     /*Wait for send to up down one rank to complete*/
     MPI_Wait(&sreqU, &sUstatus);
     /*Wait for recieve from down one rank to complete*/
     MPI_Wait(&reqU, &rUstatus);
   }

  printf("Wait complete");
  MPI_Finalize();
  return 0;
}

Все операторы печати должны быть напечатаны с соответствующими рангами. В настоящее время это делает только "отправить / recv complete" Я тестирую только на 2 процессорах атм.

1 Ответ

0 голосов
/ 25 января 2019

Несоответствие тегов

Теги должны совпадать для каждой пары операций связи, т. Е. Должны быть отправка и получение с одним и тем же тегом. В вашем случае два отправителя имеют свои собственные теги, а получатели - разные. Измените его так, чтобы при отправке вниз и при получении от вверх был один и тот же тег, и наоборот, например,

if (my_rank < my_size-1) {
    /*send top most strip up one node to be recieved as bottom halo*/
    MPI_Isend(&phi[1][0], grid_size  , MPI_DOUBLE, down, 1, MPI_COMM_WORLD, &sreqU);  
    /*recv top halo from up one node*/
    MPI_Irecv(&phi[slice+1][0], grid_size, MPI_DOUBLE, down, 2, MPI_COMM_WORLD, &reqU);
}

if (my_rank > 0) {
    /*recv top halo from down one node*/
    MPI_Irecv(&phi[0][0], grid_size , MPI_DOUBLE, up, 1, MPI_COMM_WORLD, &reqD);
    /*send bottom most strip down one node to be recieved as top halo*/
    MPI_Isend(&phi[slice][0], grid_size , MPI_DOUBLE, up, 2, MPI_COMM_WORLD, &sreqD);
}

Несоответствие объектов запроса

На границах вы ожидаете неправильных запросов, это просто исправляется путем замены MPI_Wait if body.

Ожидание нескольких неблокирующих операций

Вопреки некоторым обсуждениям в теперь удаленных ответах, правильно ждать нескольких текущих неблокирующих соединений с несколькими ожиданиями 1 .

Тем не менее, строго лучше использовать массив запросов и MPI_Waitall. Это приводит к более чистому коду, предотвратил бы ошибку смешивания запросов в первую очередь. Это также дает реализации MPI больше свободы для оптимизации. Это может выглядеть следующим образом:

MPI_Request requests[MAX_REQUESTS];
int num_requests = 0;

// ...

MPI_Isend(..., requests[num_requests++]);

// ...

MPI_Waitall(num_requests, requests, statuses);

Или вы можете использовать тот факт, что MPI_Waitall разрешает элементам массива запросов быть MPI_REQUEST_NULL. Это позволяет вам соотносить конкретные запросы и в конечном итоге является вопросом стиля.

typedef enum {
    RECV_UP, RECV_DOWN, SEND_UP, SEND_DOWN, MAX_REQUESTS
} MyRequests;

MPI_Request requests[MAX_REQUESTS];
MPI_Status statuses[MAX_REQUESTS];

if (my_rank < my_size-1) {
    /*send top most strip up one node to be recieved as bottom halo*/
    MPI_Isend(&phi[1][0], grid_size  , MPI_DOUBLE, down, 1, MPI_COMM_WORLD, &requests[SEND_DOWN]);  
    /*recv top halo from up one node*/
    MPI_Irecv(&phi[slice+1][0], grid_size, MPI_DOUBLE, down, 2, MPI_COMM_WORLD, &requests[RECV_DOWN]);
} else {
    requests[RECV_DOWN] = requests[SEND_DOWN] = MPI_REQUEST_NULL;
}

if (my_rank > 0) {
    /*recv top halo from down one node*/
    MPI_Irecv(&phi[0][0], grid_size , MPI_DOUBLE, up, 1, MPI_COMM_WORLD, &requests[RECV_UP]);
    /*send bottom most strip down one node to be recieved as top halo*/
    MPI_Isend(&phi[slice][0], grid_size , MPI_DOUBLE, up, 2, MPI_COMM_WORLD, &requests[SEND_UP]);
} else {
    requests[RECV_UP] = requests[SEND_UP] = MPI_REQUEST_NULL;
}

// ...

MPI_Waitall(MAX_REQUESTS, requests, statuses);

1: Это предусмотрено неблокирующей гарантией прогресса в стандарте MPI (3.7.4)

Progress Вызов MPI_WAIT, который завершает получение, в конечном итоге завершится и вернется, если начался соответствующий отправка, если отправка не будет удовлетворена другим получением. В частности, если соответствующая отправка является неблокирующей, то получение должно завершиться, даже если отправитель не выполнил никакого вызова для завершения отправки. Аналогичным образом, вызов к MPI_WAIT, который завершает отправку, в конечном итоге вернется, если соответствующий прием был начат, если только прием не удовлетворен другой отправкой, и даже если вызов не выполнен для завершения приема.

...