Po pierwsze, dokładna odpowiedź zależy od: (1) użycia, tj. Argumentów wejściowych funkcji, (2) jakości i szczegółów implementacji MPI oraz (3) używanego sprzętu. Często (2) i (3) są powiązane, na przykład gdy dostawca sprzętu optymalizuje MPI dla swojej sieci.
Ogólnie rzecz biorąc, łączenie kolektywów MPI jest lepsze w przypadku mniejszych wiadomości, ponieważ koszty początkowe mogą być niepraktyczne, a synchronizacja związana z blokowaniem kolektywów powinna być zminimalizowana, jeśli występuje różnica w czasie obliczeń między rozmowami. W przypadku większych wiadomości celem powinno być zminimalizowanie ilości wysyłanych danych.
Na przykład, teoretycznie, MPI_Reduce_scatter_block
powinno być lepsze niż MPI_Reduce
później MPI_Scatter
, chociaż pierwsze jest często wdrażane w odniesieniu do drugiego, tak że nie ma realnej korzyści. Istnieje korelacja między jakością implementacji a częstotliwością użycia w większości implementacji MPI, a dostawcy oczywiście optymalizują te funkcje, dla których jest to wymagane w umowie maszynowej.
Z drugiej strony, jeśli ktoś jest na Blue Gene, robiąc MPI_Reduce_scatter_block
przy użyciu MPI_Allreduce
, których nie więcej niż komunikację MPI_Reduce
i MPI_Scatter
połączony jest rzeczywiście trochę szybciej. To jest coś, co niedawno odkryłem i jest to interesujące naruszenie zasady samoreformacji wydajności w MPI (zasada ta jest opisana bardziej szczegółowo w „Samowystarczalnych wytycznych dotyczących wydajności MPI” ).
W szczególnym przypadku scatter + gromadzenie kontra zbieranie weź pod uwagę, że w pierwszym przypadku wszystkie dane muszą przechodzić do i z jednego procesu, co czyni z niego wąskie gardło, podczas gdy w przypadku zbierania danych dane mogą wpływać i wychodzić ze wszystkich szeregów natychmiast , ponieważ wszystkie stopnie mają pewne dane do wysłania do wszystkich innych stopni. Jednak wysyłanie danych ze wszystkich węzłów jednocześnie niekoniecznie jest dobrym pomysłem w niektórych sieciach.
Wreszcie najlepszym sposobem na udzielenie odpowiedzi na to pytanie jest wykonanie następujących czynności w kodzie i udzielenie odpowiedzi na pytanie eksperymentalnie.
#ifdef TWO_MPI_CALLS_ARE_BETTER_THAN_ONE
MPI_Scatter(..)
MPI_Gather(..)
#else
MPI_Allgather(..)
#endif
Jeszcze lepszą opcją jest, aby Twój kod mierzył go eksperymentalnie podczas pierwszych dwóch iteracji, a następnie użyj tej, która jest szybsza dla pozostałych iteracji:
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
NastępnieMPI_Gather
nie zapewniają tak samo jak komunikacja semantycznyMPI_Allgather
. Być może występuje nadmiarowość przy wyrażaniu operacji w jakikolwiek sposób?