Pracuję nad problemem, który można zrównoleglić za pomocą pojedynczej operacji mpi_allgather lub jednej operacji mpi_scatter i jednej operacji mpi_gather. Te operacje są wywoływane w pętli while, więc mogą być wywoływane wiele razy.
W implementacji ze schematem MPI_allgather zbieram wektor rozproszony dla wszystkich procesów w celu zduplikowanego rozwiązywania macierzy. W drugiej implementacji zbieram wektor rozproszony na jednym procesorze (węzeł główny), rozwiązuję układ liniowy na tym procesorze, a następnie rozpraszam wektor rozwiązania z powrotem na wszystkie procesy.
Jestem ciekawy, czy koszt operacji zbierania jest znacznie wyższy niż suma operacji rozproszenia i zbierania. Czy długość wiadomości odgrywa istotną rolę w jej złożoności? Czy różni się między implementacjami MPI?
Edytować:
źródło
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?MPI_Gather
następujące po nimMPI_Bcast
?Odpowiedzi:
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óźniejMPI_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życiuMPI_Allreduce
, których nie więcej niż komunikacjęMPI_Reduce
iMPI_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.
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:
źródło
Jeff ma całkowitą rację, jeśli chodzi o jedyny sposób, aby upewnić się, że jest to pomiar - w końcu jesteśmy naukowcami, i to jest pytanie empiryczne - i daje doskonałą poradę, jak wprowadzić takie pomiary. Pozwólcie, że przedstawię teraz przeciwny (a może uzupełniający) pogląd.
Należy wprowadzić rozróżnienie między pisaniem powszechnie używanego kodu a dostosowaniem go do określonego celu. Ogólnie rzecz biorąc, robimy pierwszy - budujemy nasz kod, aby: a) można go używać na wielu różnych platformach, i b) kod można aktualizować i rozszerzać w nadchodzących latach. Ale czasami robimy coś innego - mamy roczną alokację na jakąś dużą maszynę i zwiększamy do pewnego wymaganego zestawu dużych symulacji i potrzebujemy pewnego poziomu wydajności, aby wykonać to, czego potrzebowaliśmy podczas czas przyznanego przydziału.
Kiedy piszemy kod, uczynienie go powszechnie użytecznym i łatwym w utrzymaniu jest o wiele ważniejsze niż zmniejszenie o kilka procent czasu wykonywania na konkretnej maszynie. W takim przypadku właściwą rzeczą jest prawie zawsze stosowanie procedury, która najlepiej opisuje to, co chcesz zrobić - jest to na ogół najbardziej specyficzne połączenie, jakie możesz wykonać, i robi to, co chcesz. Na przykład, jeśli prosty zbieracz lub zbieracz robi wszystko, co chcesz, powinieneś tego użyć zamiast wybierać własne operacje rozproszenia / sortowania. Powody są następujące:
W tym dość powszechnym przypadku, jeśli okaże się, że jakiś kolektyw MPI działa nieuzasadnio wolno na twoim komputerze, najlepiej złożyć raport o błędzie u dostawcy MPI; nie chcesz komplikować własnego oprogramowania próbującego obejść kod aplikacji, co powinno zostać naprawione na poziomie biblioteki MPI.
Jednakże . Jeśli pracujesz w trybie „strojenia” - masz działający kod, musisz szybko uruchomić bardzo duże skale (np. Roczna alokacja) i profilujesz swój kod i odkryłem, że ta konkretna część twojego kodu jest wąskim gardłem, wtedy sensowne jest rozpoczęcie wykonywania tych bardzo specyficznych zmian. Mamy nadzieję, że nie będą to długoterminowe części kodu - najlepiej, że zmiany te pozostaną w określonej gałęzi repozytorium dla konkretnego projektu - ale być może będziesz musiał je wykonać. W takim przypadku kodowanie dwóch różnych podejść rozróżnianych przez dyrektywy preprocesora lub podejście „autotuning” dla określonego wzorca komunikacji - może mieć sens.
Więc nie zgadzam się z Jeffem, chcę tylko dodać kontekst, kiedy powinieneś być wystarczająco zainteresowany takimi względnymi pytaniami dotyczącymi wydajności, aby zmodyfikować kod, aby sobie z tym poradzić.
źródło