2013-02-12 8 views
18

Zgodnie z this website, użycie MPI::COMM_WORLD.Send(...) jest bezpieczne dla wątków. Jednak w mojej aplikacji często (nie zawsze) wpadam w zakleszczenia lub dostaję błędy segmentacji. Objęcie każdego wywołania metodami MPI::COMM_WORLD za pomocą mutex.lock() i konsekwentnie usuwa zakleszczenia, a także segfaults.Bezpieczeństwo wątku wysyłania MPI za pomocą wątków utworzonych ze std :: async

ten sposób tworzę wątki:

const auto communicator = std::make_shared<Communicator>(); 
std::vector<std::future<size_t>> handles; 
for (size_t i = 0; i < n; ++i) 
{ 
    handles.push_back(std::async(std::launch::async, foo, communicator)); 
} 
for (size_t i = 0; i < n; ++i) 
{ 
    handles[i].get(); 
} 

Communicator to klasa, która ma człon std::mutex i wyłącznie wywołuje metody takie jak MPI::COMM_WORLD.Send() i MPI::COMM_WORLD.Recv(). Nie używam żadnych innych metod wysyłania/odbierania z MPI. foo przyjmuje argument const std::shared_ptr<Commmunicator> &.

Moje pytanie: czy bezpieczeństwo nici obiecane przez MPI nie jest zgodne z wątkami utworzonymi przez std::async?

Odpowiedz

25

Bezpieczeństwo gwintów w MPI nie działa po wyjęciu z pudełka. Po pierwsze, musisz upewnić się, że twoja implementacja faktycznie obsługuje wiele wątków nawiązujących połączenia MPI na raz. W przypadku niektórych implementacji MPI, na przykład Open MPI, wymaga to skonfigurowania biblioteki ze specjalnymi opcjami w czasie kompilacji. Następnie musisz powiedzieć MPI, aby zainicjował na odpowiednim poziomie obsługi wątków. Obecnie standard MPI definiuje cztery poziomy obsługi wątków:

  • MPI_THREAD_SINGLE - oznacza, że ​​kod użytkownika jest jednotomowy. Jest to domyślny poziom, przy którym inicjowany jest MPI, jeśli użyto MPI_Init();
  • MPI_THREAD_FUNNELED - oznacza, że ​​kod użytkownika jest wielowątkowy, ale tylko główny wątek wywołuje połączenia MPI. Głównym wątkiem jest ten, który inicjalizuje bibliotekę MPI;
  • MPI_THREAD_SERIALIZED - oznacza, że ​​kod użytkownika jest wielowątkowy, ale wywołania do biblioteki MPI są serializowane;
  • MPI_THREAD_MULTIPLE - oznacza, że ​​kod użytkownika jest wielowątkowy, a wszystkie wątki mogą wykonywać połączenia MPI w dowolnym czasie bez żadnej synchronizacji.

W celu zainicjowania MPI przy pomocy gwintu, trzeba użyć MPI_Init_thread() zamiast MPI_Init():

int provided; 

MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); 
if (provided < MPI_THREAD_MULTIPLE) 
{ 
    printf("ERROR: The MPI library does not have full thread support\n"); 
    MPI_Abort(MPI_COMM_WORLD, 1); 
} 

równoważny kodu z zastąpiony (i usuwane z MPI-3) C++ wiązania:

int provided = MPI::Init_thread(argc, argv, MPI::THREAD_MULTIPLE); 
if (provided < MPI::THREAD_MULTIPLE) 
{ 
    printf("ERROR: The MPI library does not have full thread support\n"); 
    MPI::COMM_WORLD.Abort(1); 
} 

poziomy wsparcia Temat są sortowane tak: MPI_THREAD_SINGLE < MPI_THREAD_FUNNELED < MPI_THREAD_MULTIPLE, więc każdy inny podany poziom, inny niż MPI_THREAD_MULTIPLE, miałby niższą wartość liczbową - dlatego powyższy kod zapisano w ten sposób. if (...)

MPI_Init(&argc, &argv) jest odpowiednikiem MPI_Init_thread(&argc, &argv, MPI_THREAD_SINGLE, &provided). Implementacje nie są wymagane do zainicjowania dokładnie na żądanym poziomie - zamiast tego mogą być inicjowane na dowolnym innym poziomie (wyższym lub niższym), który jest zwracany w argumencie wyjściowym provided.

Więcej informacji - patrz §12.4 standardu MPI, swobodnie dostępny here.

W większości implementacji MPI, wsparcie na poziomie MPI_THREAD_SINGLE wątek jest rzeczywiście równoważne pod warunkiem, że na poziomie MPI_THREAD_SERIALIZED - dokładnie to, co można zaobserwować w Twoim przypadku.

Ponieważ nie określiłeś, która wersja MPI jest używana, oto przydatna lista.

już mówiłem, że Otwarte MPI musi być skompilowany z odpowiednimi flagami włączony żeby wesprzeć MPI_THREAD_MULTIPLE. Ale jest jeszcze jeden haczyk - komponent InfiniBand nie jest bezpieczny dla wątków, a zatem Open MPI nie będzie używał natywnej komunikacji InfiniBand, gdy zostanie zainicjowany na poziomie pełnej obsługi wątków.

Intel MPI występuje w dwóch różnych smakach - jednym z i jednym bez wsparcia dla pełnej wielowątkowości. Obsługę wielowątkową można włączyć, przekazując opcję -mt_mpi do wrappera MPI kompilatora, który umożliwia łączenie z wersją MT. Ta opcja jest również domniemana, jeśli obsługa OpenMP lub autoparalleliser jest włączona. Nie wiem, jak działa sterownik InfiniBand w IMPI, gdy włączona jest pełna obsługa wątków.

MPICH (2) nie obsługuje InfiniBand, w związku z tym jest bezpieczny dla wątków i prawdopodobnie najnowsze wersje zapewniają MPI_THREAD_MULTIPLE obsługę zaraz po wyjęciu z pudełka.

MVAPICH jest podstawą do budowy Intel MPI i obsługuje InfiniBand. Nie mam pojęcia, jak zachowuje się na pełnym poziomie obsługi wątków, gdy jest używany na komputerze z InfiniBand.

Nota o wielowątkowym wsparcia InfiniBand to ważne, ponieważ wiele klastrów obliczeniowych obecnie używać tkanin InfiniBand. Gdy składnik IB (openib BTL w Open MPI) jest wyłączony, większość implementacji MPI przechodzi na inny protokół, na przykład TCP/IP (tcp BTL w Open MPI), co powoduje znacznie wolniejszą i bardziej utajoną komunikację.

+0

Bardzo pomocna odpowiedź, dziękuję bardzo! Ponieważ muszę pracować z wieloma różnymi implementacjami mpi, niestety mam opcję użycia mojego własnego muteksa, ale to wciąż przyzwoite rozwiązanie. – stefan

+1

Zawsze masz opcję _try_ do zainicjowania MPI z pełną obsługą wątków i użycia mutex tylko, jeśli nie otrzymasz żądanego poziomu obsługi wątków. Zauważ, że możliwość wykonywania serializowanych wielowątkowych wywołań MPI w 'MPI_THREAD_SINGLE' lub' MPI_THREAD_FUNNELED' jest ** nie ** w sposób określony przez standard. –

+0

tak, to jest rzeczywiście możliwość. Jednak z punktu widzenia projektowania nie zrobiłbym tego. Obejmuje to warunkowe w całym kodzie komunikacji lub warunkowe tworzenie obiektu komunikatora. Nie podoba mi się to, więc postanowiłem w pełni polegać na własnym muteksie. To tylko trochę czystsze imho. – stefan

1

Istnieją cztery poziomy bezpieczeństwa wątków MPI, nie wszystkie są obsługiwane przez każdą implementację: MPI_THREAD_SINGLE, MPI_THREAD_FUNNELED, MPI_THREAD_SERIALIZED i MPI_THREAD_MULTIPLE. Ten ostatni, który pozwala procesowi na wiele wątków, które mogą jednocześnie wywoływać funkcje MPI, jest prawdopodobnie tym, który cię interesuje. Przede wszystkim musisz upewnić się, że twoja implementacja obsługuje MPI_THREAD_SERIALIZED.

Wymagany poziom bezpieczeństwa wątku musi być określona przez wywołanie MPI_Init_thread. Po wywołaniu MPI_Init_thread powinieneś być w stanie bezpiecznie wywoływać funkcje MPI w wątkach doładowań (POSIX) utworzonych samodzielnie.

+0

"Wywołanie MPI_Init_thread inicjalizuje środowisko wątków i faktycznie tworzy wątki." - druga część tego oświadczenia jest fałszywa. –

+0

Tak, przepraszam, poprawiłem go tuż przed pojawieniem się twojego komentarza. – piokuc

Powiązane problemy