2013-12-08 24 views
5

Sto sperimentando con MPI e stavo vagando se questo codice poteva causare un deadlock.Deadlock con MPI

MPI_Comm_rank (comm, &my_rank); 
if (my_rank == 0) { 
    MPI_Send (sendbuf, count, MPI_INT, 1, tag, comm); 
    MPI_Recv (recvbuf, count, MPI_INT, 1, tag, comm, &status); 
} else if (my_rank == 1) { 
    MPI_Send (sendbuf, count, MPI_INT, 0, tag, comm); 
    MPI_Recv (recvbuf, count, MPI_INT, 0, tag, comm, &status); 
} 

risposta

9

MPI_Send può o non può bloccare. Bloccherà finché il mittente non potrà riutilizzare il buffer del mittente. Alcune implementazioni torneranno al chiamante quando il buffer è stato inviato a un livello di comunicazione inferiore. Alcuni altri ritorneranno al chiamante quando c'è un corrispondente MPI_Recv() all'altra estremità. Quindi dipende dalla tua implementazione MPI se questo programma si bloccherà o meno.

A causa di questo programma si comporta in modo diverso tra le diverse implementazioni MPI, si può considerare rewritting così non ci saranno eventuali situazioni di stallo:

MPI_Comm_rank (comm, &my_rank); 
if (my_rank == 0) { 
    MPI_Send (sendbuf, count, MPI_INT, 1, tag, comm); 
    MPI_Recv (recvbuf, count, MPI_INT, 1, tag, comm, &status); 
} else if (my_rank == 1) { 
    MPI_Recv (recvbuf, count, MPI_INT, 0, tag, comm, &status); 
    MPI_Send (sendbuf, count, MPI_INT, 0, tag, comm); 
} 

sempre essere consapevoli del fatto che per ogni MPI_Send() ci deve essere un abbinamento MPI_Recv(), entrambi "paralleli" nel tempo. Ad esempio, questo potrebbe finire in deadlock perché l'accoppiamento di chiamate send/recv non è allineato nel tempo. Si incrociano:

RANK 0       RANK 1 
----------      ------- 
MPI_Send() ---   ---- MPI_Send() | 
       ---  ---     | 
       ------      | 
        --       | TIME 
       ------      | 
       ---  ---     | 
MPI_Recv() <--   ---> MPI_Recv() v 

Questi processi, al contrario, non si concluderà in stallo, purché ovviamente, che vi sono due processi con ranghi 0 e 1 nello stesso dominio comunicatore.

RANK 0       RANK 1 
----------      ------- 
MPI_Send() ------------------> MPI_Recv() | 
              | TIME 
              | 
MPI_Recv() <------------------ MPI_Send() v 

Il programma sopra fisso può riuscire se la dimensione del comunicatore com non consente rango 1 (solo 0). In questo modo, il if-else non prenderà il percorso else e, quindi, nessun processo sarà in ascolto per il MPI_Send() e il grado 0 sarà deadlock.

Se è necessario utilizzare il layout di comunicazione corrente, è preferibile utilizzare MPI_Isend() o MPI_Issend() per le mandate non bloccanti, evitando così il deadlock.

3

Il post di @mcleod_ideafix è molto buono. Voglio aggiungere un paio di altre cose sulle chiamate MPI non bloccanti.

Il modo in cui la maggior parte delle implementazioni MPI è che copiano i dati dal buffer utente in qualche altro posto. Potrebbe essere un buffer interno all'implementazione, potrebbe essere qualcosa di meglio nel giusto tipo di reti. Quando tali dati vengono copiati dal buffer utente e il buffer può essere riutilizzato dall'applicazione, viene restituita la chiamata MPI_SEND. Questo potrebbe essere prima che venga chiamato il corrispondente MPI_RECV oppure no. Maggiore è il numero di dati che si inviano, maggiore è la probabilità che il messaggio venga bloccato fino alla chiamata MPI_RECV.

Il modo migliore per evitare questo è utilizzare chiamate non bloccanti MPI_IRECV e MPI_ISEND. In questo modo è possibile inviare prima il numero MPI_IRECV, quindi effettuare la chiamata a MPI_ISEND. Questo evita copie extra quando arrivano i messaggi (perché il buffer per tenerli è già disponibile tramite lo MPI_IRECV) che rende le cose più veloci ed evita la situazione di deadlock. Così ora il codice sarebbe simile a questa:

MPI_Comm_rank (comm, &my_rank); 
if (my_rank == 0) { 
    MPI_Irecv (recvbuf, count, MPI_INT, 1, tag, comm, &status, &requests[0]); 
    MPI_Isend (sendbuf, count, MPI_INT, 1, tag, comm, &requests[1]); 
} else if (my_rank == 1) { 
    MPI_Irecv (recvbuf, count, MPI_INT, 0, tag, comm, &status, &requests[0]); 
    MPI_Isend (sendbuf, count, MPI_INT, 0, tag, comm, &requests[1]); 
} 
MPI_Waitall(2, request, &statuses); 
0

Come mcleod_ideafix spiegato il codice può risultare in una situazione di stallo. Qui si va: Explanation and two possible issue Solutions, one by rearranging execution order, one by async send recv calls

Heres la soluzione con chiamate asincrone:

if (rank == 0) { 
     MPI_Isend(..., 1, tag, MPI_COMM_WORLD, &req); 
     MPI_Recv(..., 1, tag, MPI_COMM_WORLD, &status); 
     MPI_Wait(&req, &status); 
} else if (rank == 1) { 
     MPI_Recv(..., 0, tag, MPI_COMM_WORLD, &status); 
     MPI_Send(..., 0, tag, MPI_COMM_WORLD); 
}