2015-08-31 18 views
6

Dopo molto google, non ho idea di cosa stia causando questo problema. Eccolo:Stallo bizzarro in MPI_Allgather

Ho una semplice chiamata a MPI_Allgather nel mio codice che ho doppio, triplo e quadruplo-controllato per essere corretto (i buffer di invio/ricezione sono dimensionati correttamente, le dimensioni di invio/ricezione nella chiamata sono corretto), ma per i "grandi" numeri di processi ottengo un deadlock o un MPI_ERR_TRUNCATE. Il comunicatore utilizzato per l'Allgather viene diviso da MPI_COMM_WORLD usando MPI_Comm_split. Per il mio test corrente, il grado 0 va a un comunicatore e i ranghi rimanenti passano a un secondo comunicatore. Per 6 ranghi totali o meno, l'Allgather funziona bene. Se uso 7 ranghi, ottengo un MPI_ERR_TRUNCATE. 8 gradi, stallo. Ho verificato che i comunicatori sono stati divisi correttamente (MPI_Comm_rank e MPI_Comm_size è corretto su tutti i ranghi per entrambi i Comm).

Ho verificato manualmente la dimensione di ciascun buffer di invio e ricezione e il numero massimo di ricezioni. La mia prima soluzione è stata quella di scambiare MPI_Allgather per un loop for di MPI_Gather ad ogni processo. Questo ha funzionato per quel caso, ma cambiando le mesh fornite al mio codice (le griglie CFD partizionate usando METIS) ha riportato il problema. Ora la mia soluzione, che non sono stato in grado di rompere (ancora), è di sostituire il Allgather con un Allgatherv, che suppongo sia più efficiente in ogni caso dal momento che ho un diverso numero di pezzi di dati inviati da ogni processo.

Ecco il codice (che spero) pertinente in questo contesto; se ho perso qualcosa, l'Allgather in questione è sulla linea 599 di this file.

// Get the number of mpiFaces on each processor (for later communication) 
    // 'nProgGrid' is the size of the communicator 'gridComm' 
    vector<int> nMpiFaces_proc(nProcGrid); 

    // This MPI_Allgather works just fine, every time  
    // int nMpiFaces is assigned on preceding lines 
    MPI_Allgather(&nMpiFaces,1,MPI_INT,nMpiFaces_proc.data(),1,MPI_INT,gridComm); 

    int maxNodesPerFace = (nDims==2) ? 2 : 4; 
    int maxNMpiFaces = getMax(nMpiFaces_proc); 
    // The matrix class is just a fancy wrapper around std::vector that 
    // allows for (i,j) indexing. The getSize() and getData() methods just 
    // call the size() and data() methods, respectively, of the underlying 
    // vector<int> object. 
    matrix<int> mpiFaceNodes_proc(nProcGrid,maxNMpiFaces*maxNodesPerFace); 
    // This is the MPI_Allgather which (sometimes) doesn't work. 
    // vector<int> mpiFaceNodes is assigned in preceding lines 
    MPI_Allgather(mpiFaceNodes.data(),mpiFaceNodes.size(),MPI_INT, 
       mpiFaceNodes_proc.getData(),maxNMpiFaces*maxNodesPerFace, 
       MPI_INT,gridComm); 

Attualmente sto usando OpenMPI 1.6.4, g ++ 4.9.2, e di un FX 8350-processore AMD 8-core con 16 GB di RAM, in esecuzione gli ultimi aggiornamenti di Elementary OS 0.3 Freya (sostanzialmente Ubuntu 14.04) . Tuttavia, ho avuto questo problema anche su un'altra macchina che utilizza CentOS, hardware Intel e MPICH2.

Qualche idea? Ho sentito che potrebbe essere possibile modificare le dimensioni del buffer interno di MPI per risolvere problemi simili, ma un tentativo rapido di farlo (come mostrato in http://www.caps.ou.edu/pipermail/arpssupport/2002-May/000361.html) non ha avuto alcun effetto.

Per riferimento, questo problema è molto simile a quello mostrato qui: https://software.intel.com/en-us/forums/topic/285074, tranne che nel mio caso, ho solo 1 processore con 8 core, su un singolo computer desktop.

UPDATE Sono riuscito a mettere insieme un esempio minimalista di questo fallimento:

#include <iostream> 
#include <vector> 
#include <stdlib.h> 
#include <time.h> 

#include "mpi.h" 

using namespace std; 

int main(int argc, char* argv[]) 
{ 
    MPI_Init(&argc,&argv); 

    int rank, nproc, newID, newRank, newSize; 
    MPI_Comm newComm; 
    MPI_Comm_rank(MPI_COMM_WORLD,&rank); 
    MPI_Comm_size(MPI_COMM_WORLD,&nproc); 

    newID = rank%2; 
    MPI_Comm_split(MPI_COMM_WORLD,newID,rank,&newComm); 
    MPI_Comm_rank(newComm,&newRank); 
    MPI_Comm_size(newComm,&newSize); 

    srand(time(NULL)); 

    // Get a different 'random' number for each rank on newComm 
    //int nSend = rand()%10000; 
    //for (int i=0; i<newRank; i++) nSend = rand()%10000; 

    /*! -- Found a set of #'s which fail for nproc=8: -- */ 
    int badSizes[4] = {2695,7045,4256,8745}; 
    int nSend = badSizes[newRank]; 

    cout << "Comm " << newID << ", rank " << newRank << ": nSend = " << nSend << endl; 

    vector<int> send(nSend); 
    for (int i=0; i<nSend; i++) 
    send[i] = rand(); 

    vector<int> nRecv(newSize); 
    MPI_Allgather(&nSend,1,MPI_INT,nRecv.data(),1,MPI_INT,newComm); 

    int maxNRecv = 0; 
    for (int i=0; i<newSize; i++) 
    maxNRecv = max(maxNRecv,nRecv[i]); 

    vector<int> recv(newSize*maxNRecv); 
    MPI_Barrier(MPI_COMM_WORLD); 
    cout << "rank " << rank << ": Allgather-ing data for communicator " << newID << endl; 
    MPI_Allgather(send.data(),nSend,MPI_INT,recv.data(),maxNRecv,MPI_INT,newComm); 
    cout << "rank " << rank << ": Done Allgathering-data for communicator " << newID << endl; 

    MPI_Finalize(); 
    return 0; 
} 

Il codice di cui sopra è stato compilato ed eseguire come:

mpicxx -std=c++11 mpiTest.cpp -o mpitest 
mpirun -np 8 ./mpitest 

con il seguente output su sia il mio CentOS a 16 core che le mie macchine Ubuntu a 8 core:

Comm 0, rank 0: nSend = 2695 
Comm 1, rank 0: nSend = 2695 
Comm 0, rank 1: nSend = 7045 
Comm 1, rank 1: nSend = 7045 
Comm 0, rank 2: nSend = 4256 
Comm 1, rank 2: nSend = 4256 
Comm 0, rank 3: nSend = 8745 
Comm 1, rank 3: nSend = 8745 
rank 5: Allgather-ing data for communicator 1 
rank 6: Allgather-ing data for communicator 0 
rank 7: Allgather-ing data for communicator 1 
rank 0: Allgather-ing data for communicator 0 
rank 1: Allgather-ing data for communicator 1 
rank 2: Allgather-ing data for communicator 0 
rank 3: Allgather-ing data for communicator 1 
rank 4: Allgather-ing data for communicator 0 
rank 5: Done Allgathering-data for communicator 1 
rank 3: Done Allgathering-data for communicator 1 
rank 4: Done Allgathering-data for communicator 0 
rank 2: Done Allgathering-data for communicator 0 

Si noti che solo 2 dei ranghi di ciascun comunicatore escono da Allgather; questo non è ciò che accade nel mio codice reale (nessun rank sul comunicatore 'rotto' esce da Allgather), ma il risultato finale è lo stesso - il codice si blocca fino a quando non lo uccido.

Immagino che questo abbia qualcosa a che fare con il diverso numero di mandate su ogni processo, ma per quanto posso dire dalla documentazione e dai tutorial MPI che ho visto, questo dovrebbe essere consentito, corretto?Ovviamente, MPI_Allgatherv è un po 'più applicabile, ma per ragioni di semplicità ho usato invece Allgather.

+2

Il tuo secondo snippet di codice non è coerente con la descrizione. Che ne dici di pubblicare il codice reale che stai utilizzando? E prova a creare MCVE. – Jeff

+0

Ho aggiornato lo snippet di codice e fornito un collegamento al file completo (2000+ linee); la mia soluzione originale MPI_Gather ha smesso di funzionare, quindi ho rimosso lo snippet. Potrei aver avuto un po 'di fortuna duplicando gli effetti in un semplice programma che alloca un vettore di dimensioni casuali (fino a un massimo di 10.000 int) su ciascun livello ed esegue il suddetto MPI_Allgather, ma non sono sicuro e probabilmente non lo farò in grado di tornare ad oggi. Lo posterò quando avrò la possibilità. – Jacob

+2

Se usi rand su diff proc, gli argomenti non corrispondono. Questo è un uso non valido di MPI. Chiamare rand su root e bcast per essere coerenti. – Jeff

risposta

4

È necessario utilizzare MPI_Allgatherv se i conteggi di input non sono identici in tutti i processi.

per essere precisi, quello che deve corrispondere è il tipo di firma count,type, dal momento che tecnicamente si può raggiungere la stessa rappresentazione fondamentale con diversi tipi di dati (ad esempio N elementi vs 1 elemento che è un tipo contiguo di N elementi), ma se si usa lo stesso argomento ovunque, che è l'uso comune dei collettivi MPI, quindi i tuoi conteggi devono corrispondere ovunque.

La porzione rilevante della norma ultime MPI (3.1) è a pagina 165:

La firma tipo associato sendcount, sendType, in un processo deve essere uguale alla firma tipo associato recvcount, recvtype in qualsiasi altro processo.