Programowanie Współbieżne MPI ( główne źródło http://pl.wikipedia.org/wiki/mpi) 1
Historia Początkowo (lata 80) różne środowiska przesyłania komunikatów dla potrzeb programowania równoległego. Niektóre wyspecjalizowane na konkretne maszyny np. Caltech N-cube. Niektóre rozwijane dla sieci UNIX workstation Np. PVM, Argonne's, P4, PICL Ohio Supercomputer Center opublikował standard przesyłania komunikatów LAM (pakiet specjalnie przystosowany do obliczeń w chemii kwantowej) zwany później TCGMSG Ta sama firma wypuściła komercyjny pakiet Express przeznaczony dla N-cube 2
Historia Początkowo różne rozwiązania dublowały się. Październik 1992 na konferencji Supercomputing 92 porozumiano się w sprawie opracowania wspólnego standardu przesyłania komunikatów i wtedy powstał MPI. Kilka bibliotek nie pasowała do sztywnych standardów np. ISIS ale weszły one do systemu klastrowego Microsoftu zwanego Wolfpack. ISIS bazował na idei wirtualnego synchronizowania procesów i nie nadawał się w zastosowaniach naukowych i komercyjnych ze względu na zbyt duże obciążenie. W MPI synchronizacja nie była wymogiem krytycznym. Pierwszy standard MPI 1 był gotowy w maju 1994 Drugi MPI 2 ukończono w 1998 roku 3
Historia MPI-2 nie zdobył zbyt wielkiej popularności gdyż w 1997 roku wprowadzono MPICH zawierający część poprawek wprowadzonych w MPI-2 MPICH i LAM to dwie najczęstsze implementacje standardu MPI Powstały też wyspecjalizowane implementacje np. Firma SGI wprowadziła na swoje platformy MPT (Message Passing Toolkit) Od 1998 wprowadzano jeszcze przez 4 lata różne korekty by w 2002 wyszła pierwsza zaawansowana implementacja. W MPI-2 zdefiniowano równoległe operacje wejścia wyjścia które pierwotnie zostały zawarte w pakiecie MPI-IO rozwijanym specjalnie na potrzeby NASA, a następnie zmodyfikowane i przeniesione do nowego MPI-2. 4
Cechy MPI jest specyfikacją biblioteki funkcji opartych na modelu wymiany komunikatów dla potrzeb programowania równoległego. Transfer danych pomiędzy poszczególnymi procesami programu wykonywanymi na procesorach maszyn będących węzłami klastra odbywa się za pośrednictwem sieci. Program w MPI składa się z niezależnych procesów operujących na różnych danych (MIMD). Każdy proces wykonuje się we własnej przestrzeni adresowej, aczkolwiek wykorzystanie pamięci współdzielonej też jest możliwe. 5
Cechy Główną zaletą standardu jest przenośność. Udostępnia on zbiór precyzyjnie zdefiniowanych metod, które mogą być efektywnie zaimplementowane. Stał się on punktem wyjściowym do stworzenia praktycznego, przenośnego, elastycznego i efektywnego narzędzia do przesyłania komunikatów (ang. message passing). Standard MPI pozwala na jego zastosowanie zarówno w komputerach równoległych, jak i heterogenicznych sieciach stacji roboczych. 6
Cechy Standard nie zabrania, aby poszczególne procesy były wielowątkowe. Nie są też udostępnione mechanizmy związane z rozłożeniem obciążenia pomiędzy poszczególne procesy, z architekturą rozkładu procesorów, z dynamicznym tworzeniem i usuwaniem procesów. Procesy są identyfikowane poprzez ich numer w grupie w zakresie 0.. groupsize - 1. 7
Główne własności MPI Cechy umożliwia efektywną komunikację bez obciążania procesora operacjami kopiowania pamięci, udostępnia funkcje dla języków C/C++, Fortan oraz Ada, specyfikacja udostępnia hermetyczny interfejs programistyczny, co pozwala na skupienie się na samej komunikacji, bez wnikania w szczegóły implementacji biblioteki i obsługi błędów, definiowany interfejs zbliżony do standardów takich jak: PVM, NX czy Express, udostępnia mechanizmy komunikacji punkt - punkt oraz grupowej, może być używany na wielu platformach, tak równoległych jak i skalarnych, bez większych zmian w sposobie działania. 8
Zalety MPI Cechy dobra efektywność w systemach wieloprocesorowych, dobra dokumentacja, bogata biblioteka funkcji, posiada status public domain, przyjął się jako standard. 9
Wady MPI Cechy statyczna konfiguracja jednostek przetwarzających, statyczna struktura procesów w trakcie realizacji programu (dotyczy to implementacji opartych o MPI-1). Wersja MPI-2 (wspierana np przez LAM 7.0.4) umożliwia dynamiczne zarządzanie strukturą procesów biorących udział w obliczeniach - MPI_Spawn(), brak wielowątkowości. 10
OpenMP Jest specyfikacją dyrektyw kompilatora, bibliotek i zmiennych środowiskowych na potrzeby przetwarzania współbieżnego w Fortranie i C/C++ opartego o mechanizm pamięci współdzielonej oraz wątki. Zaprojektowany przez głównych twórców sprzętu i oprogramowania zapewnia przenośne i skalowalne wspólne API dla różnych platform sprzętowych i programowych Ideą OpenMP jest stworzenie prostego mechanizmu wspierającego programowanie wielowątkowe, bez konieczności posiadania szczegółowej wiedzy o tworzeniu, synchronizacji i niszczeniu wątków. Zamiast tego programista otrzymuje możliwość bezpośredniego określania, które partie programu i jak mają być wykonane. 11
OpenMP Przykładowy program Hello World #include <omp.h> #include <stdio.h> int main (int argc, char *argv[]) { int id, nthreads; #pragma omp parallel private(id) { id = omp_get_thread_num(); printf("hello World from thread %d\n", id); #pragma omp barrier if ( id == 0 ) { nthreads = omp_get_num_threads(); printf("there are %d threads\n",nthreads); } } } 12
OpenMP Przykład kompilacji dla c++ intela. $ icc -openmp program.cpp -o program Uruchomienie programu dla czterech wątków: $ env OMP_NUM_THREADS=4./program OpenMP jest wspierany przez kompilator GCC od wersji 4.2. 13
MPICH ogólnodostępna, darmowa i przenośna implementacja standardu MPI. Pozwala na przekazywanie komunikatów pomiędzy aplikacjami działającymi równolegle. Nadaje się do stosowania na małych klastrach. Najnowszą wersję biblioteki MPICH można pobrać ze strony domowej projektu. Biblioteki MPICH można używać zarówno na systemach klasy MS Windows jak i Unix. Rozwinięciem MPICH jest MPICH2 Powstała także wersja tej biblioteki o nazwie MPICH-G2 pozwalająca uruchamiać aplikacje równoległe w środowiskach gridowych, z wykorzystaniem pakietu lobus Toolkit jako warstwy pośredniej. Dzięki temu rozwiązaniu aplikacja może działać na kilku klastrach rozproszonych geograficznie. 14
MPICH Najnowsza wersja biblioteki MPICH2 poza bardziej wydajnymi mechanizmami komunikacji posiada dodatkowo Wsparcie dla komunikacji jednostronnej Rozszerzoną funkcjonalność MPI-IO 15
Instalacja MPICH należy pobrać źródła programu : mpich.tar.gz Rozpakowujemy plik mpich.tar.gz # tar xfz mpich.tar.gz Będąc w katalogu /mpich wydajemy komendę: #./configure --prefix=/opt/mpich --prefix=/opt/mpich - określa ścieżkę docelową dla MPICH Kompilujemy program wydając komendę: # make Dokonujemy instalacji w katalogu podanym w opcji prefix: # make install 16
Konfiguracja MPICH W pliku machines.linux (katalog: /opt/mpich/share lub inny wybrany w opcji prefix podczas instalacji) dopisujemy nazwy hostów wchodzacych w skład klastra: hostname1:liczba_procesów hostname2:liczba_procesów hostname3:liczba_procesów gdzie: hostname - oznacza nazwa komputera wchodzącego w skład klastra, liczba_procesów - liczba procesorów danego hosta 17
MPICH Przykład komunikacji Punkt-Punkt. Zadaniem programu jest przesłanie komunikatu o treści "Komunikat od procesu 0" z procesu o numerze rank = 0 (SOURCE) do procesu o numerze rank = 1 (DEST). Program kompiluje się wprowadzając polecenie: # mpicc -o program_mpi program_mpi.c #include <stdio.h> #include "mpi.h" #define TAG 0 #define COUNT 23 #define SOURCE 0 #define DEST 1 int main(int argc, char *argv[]) { int rank, size; MPI_Status status; char Msg[]="Komunikat od procesu 0"; procesami char Recv[COUNT]; // treść komunikatu przesyłanego między 18
MPICH MPI_Init(&argc,&argv); // inicjalizacja środowiska MPI MPI_Comm_size(MPI_COMM_WORLD,&size); // ilość procesów w grupie zapisywana jest w zmiennej size MPI_Comm_rank(MPI_COMM_WORLD,&rank); // rank bieżącego procesu zapisywany jest w zmiennej rank if (size > 1) { // sprawdzane jest czy istnieje minimalna liczba procesów // Komunikat przesyłany jest od procesu o numerze rank = 0 (SOURCE) // do procesu o numerze rank = 1 (DEST) if (rank == DEST) // jeżeli procesem jest proces odbierajacy komunikat: { printf ("process %d of %d waiting for message from %d\n", rank, size, SOURCE); // odbiór komunkatu i zapis zawartości do bufora Recv MPI_Recv(Recv, COUNT, MPI_CHAR, SOURCE, TAG, MPI_COMM_WORLD, &status); printf ("process %d of %d has received: '%s'\n", rank, size, Recv); } else if (rank == SOURCE) // jeżeli procesem jest proces nadający komunikat: { printf ("process %d of %d sending '%s' to %d\n", rank, size, Msg, DEST); // wysyłanie komunkatu zawartego w buforze Msg MPI_Send(Msg, COUNT, MPI_CHAR, DEST, TAG, MPI_COMM_WORLD); } } MPI_Finalize(); return 0; } // zakończenie wykonywania procesów MPI 19
Komunikacja grupowa MPICH MPICH dostarcza kilku funkcji, ułatwiających komunikowanie się "na raz", ze wszystkimi procesami w grupie. Poza sporym ułatwieniem zapewniają one (a raczej zapewnia to MPICH),że dane zostaną przesłane w optymalny sposób (naiwny broadcast(iteracja) vs. MPI_Bcast(algorytm "drzewiasty") ). 20
MPICH MPI_Bcast Rozsyła komunikat do wszystkich procesów w grupie. int vsize = 4; float vect[vsize];... int status = MPI_Bcast( vect,vsize,mpi_float,0 /*root*/, MPI_COMM_WORLD); 21
MPICH MPI_Scatter Funkcja dzieli wektor danych wejściowych (w procesie oznaczonym jako root) i rozsyła je tak,że procesowi o randze N jest przesyłana N-ta część wektora danych. int vsize = 4; float matrix[vsize*vsize]; float rvect[vsize] int status = MPI_Scatter(matrix, vsize, MPI_FLOAT, rvect, vsize, MPI_FLOAT, 0 /*root*/, MPI_COMM_WORLD); // do 4-ech procesów 22
MPICH MPI_Gather Funkcja odwrotna do MPI_Scatter. Zbiera dane w procesie oznaczonym root. Ustawia je w kolejkę zgodnie z rangą nadsyłającego procesu. int vsize = 4; float matrix[vsize*vsize]; float rvect[vsize] int status = MPI_Gather(rvect, vsize, MPI_FLOAT, matrix, vsize, MPI_FLOAT, 0 /*root*/,mpi_comm_world); // od 4- ech procesów 23
MPICH MPI_Reduce Funkcja wykonuje zdefiniowaną operację na wysłanych przez procesy danych. Możliwe operacje: MPI_MAX - maximum MPI_MIN - minimum MPI_SUM - suma MPI_PROD - produkt MPI_LAND - logiczny AND MPI_LOR - logiczny OR MPI_LXOR - logiczny XOR MPI_BAND - bitowy AND MPI_BOR - bitowy OR MPI_BXOR - bitowy XOR int val = 1, proccnt; int status = MPI_Reduce( &val,&proccnt,1,mpi_int,mpi_sum,0 /*root*/,mpi_comm_world ); // alternatywna metoda liczenia procesów 24
MPICH Przykład komunikacji grupowej realizowanej za pośrednictwem funkcji MPI_Scatter i MPI_Reduce. Działanie programu polega na rozesłaniu poszczególnych wierszy macierzy zawartej w buforze sendbuf do czterech procesów w grupie. Program kompiluje się wprowadzając polecenie: # mpicc -o program_mpi program_mpi.c 25
#include "mpi.h" #include <stdio.h> #define SIZE 4 main(int argc, char *argv[]) { int numtasks, rank, sendcount, recvcount, source; float sendbuf[size][size] = { {1.0, 2.0, 3.0, 4.0}, {5.0, 6.0, 7.0, 8.0}, {9.0, 10.0, 11.0, 12.0}, {13.0, 14.0, 15.0, 16.0} }; // inicjalizacja bufora do wysyłki float recvbuf[size]; MPI_Init(&argc,&argv); // inicjalizacja środowiska MPI MPI_Comm_rank(MPI_COMM_WORLD, &rank); // rank bieżącego procesu zapisywany jest w zmienej rank MPI_Comm_size(MPI_COMM_WORLD, &numtasks); // ilość procesów w grupie zapisywana jest w zmiennej size if (numtasks == SIZE) { // jeżeli w grupie są dokładnie 4 procesy: source = 1; sendcount = SIZE; recvcount = SIZE; // rozsyłanie do procesów w grupie komunikatu zawartego w buforze sendbuf MPI_Scatter(sendbuf,sendcount,MPI_FLOAT,recvbuf,recvcount, MPI_FLOAT,source,MPI_COMM_WORLD); printf("rank= %d Results: %f %f %f %f\n",rank,recvbuf[0], recvbuf[1], recvbuf[2], recvbuf[3]); int val = 1, proccnt; MPI_Reduce( &val,&proccnt,1,mpi_int,mpi_sum,source,mpi_comm_world ); // suma,czyli liczba procesów } else printf("must specify %d processors. Terminating.\n",SIZE); MPI_Finalize(); // zakończenie wykonywania procesów MPI 26 }
MPICH Więcej informacji http://www-unix.mcs.anl.gov/mpi/mpich/ Strona domowa projektu http://www-unix.mcs.anl.gov/mpi/www/ Manual do funkcji 27