Di quali funzionalità hanno bisogno gli utenti da un'interfaccia MPI C ++?


28

La versione 3.0 dello standard MPI ha eliminato formalmente l'interfaccia C ++ (era precedentemente deprecata). Sebbene le implementazioni possano ancora supportarlo, le funzionalità nuove in MPI-3 non hanno un'interfaccia C ++ definita nello standard MPI. Vedere http://blogs.cisco.com/performance/the-mpi-c-bindings-what-happened-and-why/ per ulteriori informazioni.

La motivazione per rimuovere l'interfaccia C ++ da MPI era che non aveva un valore significativo sull'interfaccia C. Vi erano pochissime differenze oltre a "s / _ / :: / g" e molte funzionalità alle quali gli utenti di C ++ sono abituati non sono state utilizzate (ad esempio, determinazione automatica del tipo tramite modelli).

Come qualcuno che partecipa al forum MPI e lavora con una serie di progetti C ++ che hanno implementato la propria interfaccia C ++ per le funzioni MPI C, vorrei sapere quali sono le caratteristiche desiderabili di un'interfaccia C ++ per MPI. Anche se non mi impegno per nulla, sarei interessato a vedere l'implementazione di un'interfaccia MPI C ++ autonoma che soddisfi le esigenze di molti utenti.

E sì, ho familiarità con Boost :: MPI ( http://www.boost.org/doc/libs/1_54_0/doc/html/mpi.html ) ma supporta solo le funzionalità MPI-1 e il modello di serializzazione sarebbe estremamente difficile da supportare per RMA.

Un'interfaccia C ++ per MPI che mi piace è quella di Elemental ( https://github.com/poulson/Elemental/blob/master/src/core/imports/mpi.cpp ), quindi forse le persone possono fornire qualche pro e contro che approccio. In particolare, penso che MpiMap risolva un problema essenziale.


Non penso che questo sia il posto adatto per una domanda del genere.
Jack Poulson,

Puoi darci qualche motivo? Molte delle domande MPI su questo sito mi suggeriscono che le persone qui sono pronte a rispondere a questa domanda. Inoltre, 0,2 voti al minuto suggeriscono che altre persone non sono d'accordo con la tua valutazione. In ogni caso, sarebbe più utile suggerire un posto alternativo per pubblicare questo post se non ti piace la sede attuale.
Jeff,

La domanda è preziosa e penso che potrebbe ottenere alcune risposte preziose su più ampie mailing list di scienze computazionali, se è in campo lì. (Forse NA-digest, SIAM-CSE o anche un post pubblico su G +?) Questa domanda potrebbe non essere adatta per un sito di Stack Stack perché è soggettiva (vedi scicomp.stackexchange.com/help/dont-ask ) . Finché le risposte sono concrete e si concentrano su casi d'uso specifici (senza ripetizioni significative o sovrapposizioni), penso che valga la pena tenerlo aperto.
Geoff Oxberry,

@Jeff: la domanda mi sembra molto simile a un sondaggio. Non dispongo che sia prezioso, ma non vedo che ci sia una risposta accettata. Una domanda del genere sarebbe fuori dal comune per il forum MPI?
Jack Poulson,

@JackPoulson Non voglio sapere che cosa gli attuatori ritengano sia la risposta giusta; Voglio sapere di cosa hanno bisogno gli scienziati computazionali. A questo proposito, la domanda ha risposte obiettive. Non c'è una risposta giusta, ma ciò non significa che sia una situazione soggettiva.
Jeff,

Risposte:


17

Consentitemi innanzitutto di rispondere al motivo per cui penso che le interfacce C ++ per MPI non abbiano generalmente avuto un esagerato successo, avendo riflettuto sul problema per molto tempo quando ho cercato di decidere se dovremmo semplicemente usare i binding C standard di MPI o basarci su qualcosa di livello superiore :

Quando guardi i codici MPI del mondo reale (diciamo, PETSc o nel mio caso.II), si scopre che forse sorprendentemente, il numero di chiamate MPI non è in realtà molto grande. Ad esempio, nelle 500k linee di offerta.II, ci sono solo ~ 100 chiamate MPI. Una conseguenza di ciò è che il dolore coinvolto nell'uso di interfacce di livello inferiore come i collegamenti MPI C, non è troppo grande. Viceversa, non si otterrebbero così tanti vantaggi utilizzando interfacce di livello superiore.

La mia seconda osservazione è che su molti sistemi sono installate più librerie MPI (diverse implementazioni MPI o versioni diverse). Ciò rappresenta una difficoltà significativa se si desidera utilizzare, ad esempio, boost :: mpi che non consiste solo di file di intestazione: o devono essere presenti anche più installazioni di questo pacchetto, oppure è necessario costruirlo come parte del progetto che utilizza boost :: mpi (ma questo è di nuovo un problema in sé, dato che boost usa il proprio sistema di build, che è diverso da qualsiasi altra cosa).

Quindi penso che tutto questo abbia cospirato contro l'attuale crop di interfacce C ++ per MPI: i vecchi binding MPI C ++ non offrivano alcun vantaggio e pacchetti esterni avevano difficoltà con il mondo reale.

Detto questo, ecco cosa penso siano le caratteristiche killer che vorrei avere da un'interfaccia di livello superiore:

  • Dovrebbe essere generico. Dover specificare il tipo di dati di una variabile non è decisamente C ++. Naturalmente, porta anche a errori. La classe MpiMap di Elemental sarebbe già un bel primo passo (anche se non riesco a capire perché diavolo la MpiMap::typevariabile non sia const statica, quindi è possibile accedervi senza creare un oggetto).

  • Dovrebbe disporre di strutture per lo streaming di tipi di dati arbitrari.

  • Le operazioni che richiedono un MPI_Opargomento (ad es. Riduzioni) dovrebbero integrarsi perfettamente con l' std::functioninterfaccia di C ++ , in modo che sia facile semplicemente passare un puntatore a funzione (o un lambda!) Piuttosto che dover goffamente registrare qualcosa.

boost :: mpi in realtà soddisfa tutti questi. Penso che se fosse una libreria di sola intestazione, sarebbe molto più popolare in pratica. Sarebbe anche utile se supportasse le funzioni post-MPI 1.0, ma siamo onesti: questo copre la maggior parte di ciò di cui abbiamo bisogno la maggior parte del tempo.


Uno dei problemi con la serializzazione in Boost :: MPI è che non funziona con un lato (aka RMA). Pensi che sarà possibile creare tipi di dati MPI per gli oggetti C ++ a cui gli utenti sono interessati? Certo, in teoria tutto dovrebbe essere supportato ma preferisco iniziare con un obiettivo più pragmatico.
Jeff,

Penso che sia un errore pensare che il tipo di dati serializzato possa mai funzionare con comunicazioni unilaterali. Ciò presuppone che la serializzazione implichi solo l'imballaggio dei dati in una stringa e dall'altro lato la decompressione di nuovo. Ma le funzioni di serializzazione possono fare molto di più (ad esempio, tenere traccia dei puntatori su altri oggetti, contare i byte che sono stati serializzati, ecc.) Di quanto ci si possa aspettare che funzioni se non è possibile eseguire nulla sull'host di destinazione. Il mio punto di vista è che la serializzazione in stile C ++ e la comunicazione unilaterale non sono altro che un inizio. Penso che nessuno dovrebbe aspettarsi che funzioni, quindi poco ci mancherebbe.
Wolfgang Bangerth,

Ciao Wolfgang, hai ragione sul fatto che MpiMap sarebbe più elegante se usasse una variabile membro const statica. Ho riorganizzato l'implementazione: github.com/poulson/Elemental/commit/…
Jack Poulson

Sì, molto più bello :-)
Wolfgang Bangerth,

+1 su non molte chiamate mpi @WolfgangBangerth. Fondamentalmente, mpi dovrebbe rendere i calcoli più veloci, vuoi minimizzare le chiamate mpi perché ti costano!
Charles

6

Per far rotolare la palla, ecco due dei miei bisogni:

  • L'interfaccia dovrebbe essere in grado di eliminare argomenti ridondanti o superflui, ad esempio MPI_IN_PLACE.
  • L'interfaccia dovrebbe rilevare automaticamente i tipi di dati incorporati su MpiMap di Elemental.
  • Se / quando possibile, i tipi di dati definiti dall'utente dovrebbero essere costruiti per le classi.

5

La mia lista in nessun ordine di preferenza particolare. L'interfaccia dovrebbe:

  • essere solo intestazione, senza dipendenze ma <mpi.h>, e la libreria standard,
  • essere generico ed estensibile,
  • essere non-blocking solo (se si desidera bloccare, quindi bloccare in modo esplicito, non per impostazione predefinita),
  • consentire il concatenamento basato su continuazione di operazioni non bloccanti,
  • supportare la serializzazione estensibile ed efficiente (tipo Boost.Fusion, in modo che funzioni con RMA),
  • avere penalità di astrazione pari a zero (ovvero essere veloce almeno quanto l'interfaccia C),
  • essere al sicuro (si chiama il distruttore di un futuro non pronto? -> std :: terminate!),
  • avere una DEBUGmodalità forte con tonnellate di affermazioni,
  • estremamente sicuro (non più ints / void * per tutto, diamine voglio che i tag siano tipi!),
  • dovrebbe funzionare con lambdas (ad es. tutti riducono + lambda),
  • utilizzare le eccezioni costantemente come meccanismo di segnalazione e gestione degli errori (niente più codici di errore! niente più argomenti di output delle funzioni!),
  • MPI-IO dovrebbe offrire un'interfaccia I / O non bloccante nello stile di Boost.AFIO,
  • e basta seguire le buone pratiche moderne di progettazione dell'interfaccia C ++ (definire tipi regolari, funzioni non amiche per i non membri, giocare bene con la semantica dei movimenti, operazioni con la gamma di supporto, ...)

extra:

  • mi permetta di scegliere l'esecutore dell'ambiente MPI, ovvero quale pool di thread utilizza. In questo momento puoi avere applicazioni con un mix di OpenMP, MPI, CUDA e TBB ... tutto allo stesso tempo, dove ogni runtime pensa di possedere l'ambiente e quindi chiedere al sistema operativo i thread ogni volta che si sentono come esso. Sul serio?

  • usa la convenzione di denominazione STL (e Boost). Perché? Tutti i programmatori C ++ lo sanno.

Voglio scrivere codice in questo modo:

auto buffer = some_t{no_ranks};
auto future = gather(comm, root(comm), my_offsets, buffer)
              .then([&](){
                /* when the gather is finished, this lambda will 
                   execute at the root node, and perform an expensive operation
                   there asynchronously (compute data required for load 
                   redistribution) whose result is broadcasted to the rest 
                   of the communicator */
                return broadcast(comm, root(comm), buffer);
              }).then([&]() {
                /* when broadcast is finished, this lambda executes 
                   on all processes in the communicator, performing an expensive
                   operation asynchronously (redistribute the load, 
                   maybe using non-blocking point-to-point communication) */
                 return do_something_with(buffer);
              }).then([&](auto result) {
                 /* finally perform a reduction on the result to check
                    everything went fine */
                 return all_reduce(comm, root(comm), result, 
                                  [](auto acc, auto v) { return acc && v; }); 
              }).then([&](auto result) {
                  /* check the result at every process */
                  if (result) { return; /* we are done */ }
                  else {
                    root_only([](){ write_some_error_log(); });
                    throw some_exception;
                  }
              });

/* Here nothing has happened yet! */

/* ... lots and lots of unrelated code that can execute concurrently 
   and overlaps with communication ... */

/* When we now call future.get() we will block 
   on the whole chain (which might have finished by then!).
*/

future.get();

Pensa come si potrebbero concatenare tutte queste operazioni usando MPI_C request. Dovresti testare a più (o ogni singolo) passaggio intermedio attraverso un sacco di codice non correlato per vedere se puoi far avanzare la catena senza bloccare .


Questo è un intenso elenco di requisiti. Alcuni di essi sono impossibili per tutti gli scopi pratici (es. Sostenere lambda nelle riduzioni). Tuttavia, nel complesso penso che sia il tipo di cosa che la comunità MPI dovrebbe aspirare a supportare.
Jeff,

Per quanto riguarda i thread e l'ambiente di runtime, MPI non utilizza internamente né thread né thread del sistema operativo (POSIX, Windows o Solaris, a seconda del sistema operativo). Non sono del tutto sicuro di aver compreso i requisiti qui.
Jeff,

Il problema è che MPI può arbitrariamente richiedere thread dal sistema operativo. La mia applicazione ha un pool di thread e vorrei che MPI richiedesse quei thread dal mio pool di thread. Questo al momento non è possibile (e in genere non è un problema), a meno che non si disponga di un'applicazione che utilizza OpenMP, TBB e MPI, ciascuno con i propri pool di thread, ciascuno con un numero di core 4x.
gnzlbg,

1
MPI può, ma generalmente non utilizza internamente thread del sistema operativo. In ogni caso con cui ho familiarità (MPICH (2), MVAPICH2, OpenMPI, CrayMPI), solo un'opzione di runtime fornita dall'utente fa sì che ciò avvenga, ovvero l'impostazione predefinita è che non lo fa. Blue Gene / Q MPI è un'eccezione ma in una forma tale da non essere rilevante per questa discussione.
Jeff,

Grazie @Jeff! Potresti approfondire come MPI gestisce più chiamate non bloccanti mentre usi un singolo thread? Usa coroutine / fili / fibre verdi?
gnzlbg,

2

Personalmente, non mi dispiace davvero chiamare lunghe funzioni in stile C per la ragione esatta menzionata da Wolfgang; ci sono davvero pochi posti in cui è necessario chiamarli e anche in questo caso, vengono quasi sempre racchiusi in un codice di livello superiore.

Le uniche cose che mi infastidiscono davvero con l'MPI di tipo C sono i tipi di dati personalizzati e, in misura minore, le operazioni personalizzate (perché le utilizzo meno spesso). Per quanto riguarda i tipi di dati personalizzati, direi che una buona interfaccia C ++ dovrebbe essere in grado di supportare un modo generico ed efficiente di gestirlo, molto probabilmente attraverso la serializzazione. Questo è ovviamente il percorso che boost.mpiha preso, che se stai attento , fa risparmiare molto tempo.

Per quanto riguarda boost.mpile dipendenze extra (in particolare boost.serializationche di per sé non è solo intestazione), di recente mi sono imbattuto in una libreria di serializzazione C ++ solo intestazione chiamata cereale che sembra promettente; a condizione che richieda un compilatore conforme a C ++ 11. Potrebbe valere la pena esaminarlo e usarlo come base per qualcosa di simile a boost.mpi.


Nota che non stavo necessariamente cercando qualcosa da inserire nello standard MPI. Concordo pienamente sul fatto che l'MPI dovrebbe quasi sempre "rimanere avvolto da un codice di livello superiore", quindi quello che mi chiedo è, che aspetto ha quel codice di livello superiore? Elemental ha un approccio gradevole, ma è il migliore per tutti i casi? Se si volesse avere un'interfaccia C ++ per MPI che cercasse di rendere felice un numero molto elevato di persone, come sarebbe?
Jeff,

@Jeff. Per me: 1. Mi piace essere in grado di inviare facilmente tipi di dati personalizzati. 2. Mi piace essere in grado di eseguire facilmente la riduzione con operazioni personalizzate. Inoltre penso che io abbia
oscillato

Non vedo come C ++ faccia qualcosa di magico wrt (2). Cosa immagini qui?
Jeff,

@Jeff Stavo pensando a qualcosa di simile a come thrustfa per le riduzioni: docs.thrust.googlecode.com/hg/group__reductions.html
GradGuy

-1

Il progetto github easyLambda fornisce un'interfaccia di alto livello a MPI con C ++ 14.

Penso che il progetto abbia obiettivi simili e darà un'idea delle cose che possono essere e che si stanno facendo in quest'area usando il C ++ moderno. Guida di altri sforzi e di easyLambda stesso.

I benchmark iniziali su prestazioni e linee di codice hanno mostrato risultati promettenti.

inserisci qui la descrizione dell'immagine

Di seguito è una breve descrizione delle funzionalità e dell'interfaccia che fornisce.

L'interfaccia si basa sulla programmazione del flusso di dati e sulle operazioni dell'elenco funzionale che forniscono parallelismo intrinseco. Il parallelismo è espresso come proprietà di un compito. L'allocazione del processo e la distribuzione dei dati per l'attività possono essere richieste con una proprietà .prll (). Ci sono un buon numero di esempi nella pagina web e nel repository di codici che includono la post-elaborazione della dinamica molecolare LAMMPS, la soluzione di differenza finita esplicita all'equazione del calore, la regressione logistica ecc. Ad esempio, il problema della diffusione del calore discusso nell'articolo HPC sta morendo ... può essere espresso in ~ 20 righe di codice.

Spero che vada bene fornire collegamenti piuttosto che aggiungere maggiori dettagli e codici di esempio qui.

Disclamer: sono l'autore della biblioteca. Credo di non fare alcun male nella speranza di ottenere un feedback costruttivo sull'interfaccia attuale di easyLambda che potrebbe essere vantaggioso per easyLambda e qualsiasi altro progetto che persegue obiettivi simili.


La domanda dice " Stiamo cercando risposte lunghe che forniscano una spiegazione e un contesto. Non limitarti a dare una risposta di una riga; spiega perché la tua risposta è giusta, idealmente con citazioni. Le risposte che non includono spiegazioni potrebbero essere rimosse . ". Perché pensi che la tua risposta sia abbastanza completa da adattarsi a questa descrizione?
Nicoguaro

Sto indicando un progetto che fornisce un'interfaccia simile a ciò che l'OP sta chiedendo. Posso fornire dettagli sulla motivazione e le caratteristiche del progetto nella risposta stessa e lasciare che l'OP e gli altri leggano e suggeriscano cosa ne pensano. Tuttavia, ho scelto di fornire solo collegamenti. Ho capito il tuo punto. Vorrei aggiungere del testo con riferimenti alla risposta.
Utkarsh Bhardwaj,

@UtkarshBhardwaj: Alcuni commenti: (1) Grazie per aver scritto una libreria che interfaccia C ++ con MPI. È un'impresa significativa e sembra che tu ci abbia dedicato molto lavoro. (2) Esaminando rapidamente i documenti (di nuovo, bel lavoro), ciò che risalta sono le lunghe catene di comandi di metodo utilizzati, che sembra stilisticamente ... doloroso per il debug, anche se li hai formattati bene. (3) Le astrazioni presumono un paradigma funzionale, che sembra utile per attività simili alla riduzione delle mappe, ma come qualcuno che programma in MPI, non voglio rielaborare i miei algoritmi e allontanarmi troppo dalle interfacce che conosco.
Geoff Oxberry,

(4) Non vedo comunicatori da nessuna parte nell'esempio, il che mi porta a sospettare che stai usando MPI_COMM_WORLD per tutto e limita il potenziale della tua biblioteca. (5) Le astrazioni sembrano basarsi sulle astrazioni MPI e potrebbero avere un backend diverso. (6) Basato su (3) - (5), non credo che questa libreria sia un'interfaccia MPI e, di conseguenza, ritengo che questo commento sia fuori tema.
Geoff Oxberry,

@Geoff grazie per il prezioso feedback, molto apprezzato. Rispondi a punti specifici: 2) Il concatenamento è talvolta chiamato ExpressionBuilder . È un modo comune di esprimere la composizione in stile funzionale. Non è necessario scrivere in questo stile in ezl. È possibile scrivere prima le singole unità e poi comporle, controllare [esempio] ( goo.gl/YzaL0k ). 3) So che è difficile passare dal flusso di controllo e imperativo al flusso di dati e funzionale. Ognuno ha i suoi pro e contro. Tuttavia, credo che quest'ultimo debba essere esplorato di più per conoscerne la vera efficacia.
Utkarsh Bhardwaj,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.