Innanzitutto, la risposta esatta dipende da: (1) utilizzo, ovvero argomenti di input della funzione, (2) qualità e dettagli dell'implementazione MPI e (3) l'hardware che si sta utilizzando. Spesso, (2) e (3) sono correlati, ad esempio quando il fornitore di hardware ottimizza MPI per la propria rete.
In generale, la fusione dei collettivi di MPI è migliore per i messaggi più piccoli, poiché i costi di avvio possono essere non banali e la sincronizzazione comportata dal blocco dei collettivi dovrebbe essere ridotta al minimo se si verificano variazioni nel tempo di calcolo tra le chiamate. Per i messaggi più grandi, l'obiettivo dovrebbe essere quello di ridurre al minimo la quantità di dati inviati.
Ad esempio, in teoria, MPI_Reduce_scatter_block
dovrebbe essere migliore di quello MPI_Reduce
seguito MPI_Scatter
, sebbene il primo sia spesso implementato in termini di secondo, in modo tale che non vi sia alcun vantaggio reale. Esiste una correlazione tra qualità dell'implementazione e frequenza d'uso nella maggior parte delle implementazioni di MPI, e ovviamente i fornitori ottimizzano quelle funzioni per le quali ciò è richiesto dal contratto con la macchina.
D'altra parte, se uno è su un Blue Gene, facendo MPI_Reduce_scatter_block
uso MPI_Allreduce
, che fa più la comunicazione di MPI_Reduce
e MPI_Scatter
combinati, in realtà è piuttosto un po 'più veloce. Questo è qualcosa che ho scoperto di recente ed è un'interessante violazione del principio di auto-coerenza delle prestazioni in MPI (questo principio è descritto più dettagliatamente in "Linee guida per le prestazioni MPI auto-coerenti" ).
Nel caso specifico di scatter + gather contro allgather, considera che nel primo, tutti i dati devono andare da e verso un singolo processo, il che ne fa il collo di bottiglia, mentre nell'allgather i dati possono fluire dentro e fuori da tutti i ranghi immediatamente , poiché tutti i gradi hanno alcuni dati da inviare a tutti gli altri gradi. Tuttavia, l'invio di dati da tutti i nodi contemporaneamente non è necessariamente una buona idea su alcune reti.
Infine, il modo migliore per rispondere a questa domanda è eseguire le seguenti operazioni nel codice e rispondere alla domanda mediante esperimento.
#ifdef TWO_MPI_CALLS_ARE_BETTER_THAN_ONE
MPI_Scatter(..)
MPI_Gather(..)
#else
MPI_Allgather(..)
#endif
Un'opzione ancora migliore è che il tuo codice lo misuri sperimentalmente durante le prime due iterazioni, quindi usa quello che è più veloce per le restanti iterazioni:
const int use_allgather = 1;
const int use_scatter_then_gather = 2;
int algorithm = 0;
double t0 = 0.0, t1 = 0.0, dt1 = 0.0, dt2 = 0.0;
while (..)
{
if ( (iteration==0 && algorithm==0) || algorithm==use_scatter_then_gather )
{
t0 = MPI_Wtime();
MPI_Scatter(..);
MPI_Gather(..);
t1 = MPI_Wtime();
dt1 = t1-t0;
}
else if ( (iteration==1 && algorithm==0) || algorithm==use_allgather)
{
t0 = MPI_Wtime();
MPI_Allgather(..);
t1 = MPI_Wtime();
dt2 = t1-t0;
}
if (iteration==1)
{
dt2<dt1 ? algorithm=use_allgather : algorithm=use_scatter_then_gather;
}
}
MPI_Scatter
seguito daMPI_Gather
non fornisce la stessa comunicazione semantica diMPI_Allgather
. Forse c'è ridondanza quando si esprime l'operazione in entrambi i modi?