MPICH

Z Wikipedii, wolnej encyklopedii
Skocz do: nawigacji, wyszukiwania

MPICH to 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 Globus Toolkit jako warstwy pośredniej. Dzięki temu rozwiązaniu aplikacja może działać na kilku klastrach rozproszonych geograficznie.

Charakterystyka[edytuj | edytuj kod]

MPICH jest implementacją biblioteki sterowania procesem obliczeń równoległych na maszyny klasy PC. Protokół ten przeznaczony jest do sterowania procesem obliczeń równoległych w sieciach rozproszonych. Biblioteka procedur MPICH jest dostępna bezpłatnie. Najnowsza wersja tej biblioteki MPICH2 poza zapewnieniem bardziej wydajnych mechanizmów komunikacji posiada dodatkowo:

  • wsparcie dla komunikacji jednostronnej
  • rozszerzoną funkcjonalność MPI-IO

Instalacja[edytuj | edytuj kod]

Opis instalacji MPICH (jednej z implementacji MPI)

1. Na początku należy pobrać źródła programu : mpich.tar.gz

2. Rozpakowujemy plik mpich.tar.gz
# tar xfz mpich.tar.gz

3. Będąc w katalogu /mpich wydajemy komendę:
# ./configure --prefix=/opt/mpich
--prefix=/opt/mpich - określa ścieżkę docelową dla MPICH

4. Kompilujemy program wydając komendę:
# make

5. Dokonujemy instalacji w katalogu podanym w opcji prefix:
# make install

Konfiguracja[edytuj | edytuj kod]

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

Przesyłanie komunikatów[edytuj | edytuj kod]

Komunikacja punkt - punkt[edytuj | edytuj kod]

Przykład[edytuj | edytuj kod]

Przykład komunikacji międzyprocesowej typu 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).

#include <stdio.h>
#include "mpi.h"
#define TAG 0 #define COUNT 25 #define SOURCE 0 #define DEST 1
int main(int argc, char *argv[]) { int rank, size; MPI_Status status;
char Msg[]="Komunikat od procesu 0"; // treść komunikatu przesyłanego między procesami char Recv[COUNT]; MPI_Init(&argc,&argv); // inicjalizacja środowiska MPI manual MPI_Comm_size(MPI_COMM_WORLD,&size); // liczba procesów w grupie zapisywana jest w zmiennej size manual MPI_Comm_rank(MPI_COMM_WORLD,&rank); // rank bieżącego procesu zapisywany jest w zmienej rank manual
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 manual 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 manual MPI_Send(Msg, COUNT, MPI_CHAR, DEST, TAG, MPI_COMM_WORLD); } } MPI_Finalize(); // zakończenie wykonywania procesów MPI manual return 0; }

Program kompiluje się wprowadzając polecenie:
# mpicc -o program_mpi program_mpi.c

Komunikacja grupowa[edytuj | edytuj kod]

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") ).

MPI_Bcast[edytuj | edytuj kod]

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); // man

MPI_Scatter[edytuj | edytuj kod]

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!!! man

MPI_Gather[edytuj | edytuj kod]

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 !!!man

MPI_Reduce[edytuj | edytuj kod]

Funkcja wykonuje zdefiniowaną operację na wysłanych przez procesy danych. Możliwe operacje:

  1. MPI_MAX - maximum
  2. MPI_MIN - minimum
  3. MPI_SUM - suma
  4. MPI_PROD - produkt
  5. MPI_LAND - logiczny AND
  6. MPI_LOR - logiczny OR
  7. MPI_LXOR - logiczny XOR
  8. MPI_BAND - bitowy AND
  9. MPI_BOR - bitowy OR
  10. 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 :) man

Przykład[edytuj | edytuj kod]

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.

#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 manual MPI_Comm_rank(MPI_COMM_WORLD, &rank); // rank bieżącego procesu zapisywany jest w zmienej rank manual MPI_Comm_size(MPI_COMM_WORLD, &numtasks); // liczba procesów w grupie zapisywana jest w zmiennej size manual
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 manual 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 man } else printf("Must specify %d processors. Terminating.\n",SIZE);
MPI_Finalize(); // zakończenie wykonywania procesów MPI manual }

Program kompiluje się wprowadzając polecenie:
# mpicc -o program_mpi program_mpi.c

Typy danych[edytuj | edytuj kod]

Podstawowe typy danych[edytuj | edytuj kod]

 

Typy podstawowe
MPI C Rozmiar
MPI_CHAR signed char 1 bajt
MPI_SHORT signed short int 2 bajty
MPI_INT signed int 2 bajty
MPI_LONG signed long int 4 bajty
MPI_UNSIGNED_CHAR unsigned char 1 bajt
MPI_UNSIGNED_SHORT unsigned short int 1 bajt
MPI_UNSIGNED unsigned int 2 bajty
MPI_UNSIGNED_LONG unsigned long int 4 bajty
MPI_FLOAT float 4 bajty
MPI_DOUBLE double 8 bajtów
MPI_LONG_DOUBLE long double 8, 10 lub 12 bajtów
MPI_BYTE 8 binarnych cyfr (8 bitów) 1 bajt
MPI_PACKED dane spakowane lub rozpakowane z użyciem MPI_Pack()/ MPI_Unpack()

W MPI możemy tworzyć własne typy danych. Najczęściej chcemy, aby typ przesyłanych danych był różny od standardowych typów zdefiniowanych w bibliotece MPI. W MPI zdecydowano się na rozwiązanie, w którym programista ma możliwość tworzenia nowych typów danych w czasie wykonywania programu. Takie typy danych nazywane są POCHODNYMI TYPAMI DANYCH lub TYPAMI UŻYTKOWNIKA. Pochodne typy danych tworzymy z podstawowych typów danych.

Typy pochodne[edytuj | edytuj kod]

Każdy typ danych w MPI jest określony przez tablice typemap podającą dla każdego elementu parami typ podstawowy i przesunięcie w bajtach

Typemap={(type0 ,disp0 ),...,(typen-1 ,dispn-1 )}

przykład:   MPI_INT         (int,0)


Definicje Dolnej i Górnej Granicy oraz Rozpiętości i Rozmiaru Typów Danych

lb(Typemap) = minj (dispj)
ub(Typemap) = maxj (dispj+sizeof(typej))+pad
extent(Typemap) = ub(Typemap)-lb(Typemap) 

„pad” określa poprawkę ze wzgędu na rozmieszczenie danych w pamięci komputera, w większosci przypadków wymagane jest aby dane określonego typu były umieszczone pod adresami będącymi wielokrotnością rozmiaru danego typu, np. int zajmuje 4 bajty więc adres powinien byc podzielny przez 4, więc dla:

{(int,0),(char,4)}
lb=min(0,4)=0    
ub=max(0+4,4+1)+pad=5+pad=8
extent=5+pad=8
rozmiar=5


Pochodne typy danych[edytuj | edytuj kod]

Contiguous
Jest to najprostszy typ pochodny; elementy są kopiami danego typu wejściowego a ich pozycje są wielokrotnościami jego rozpiętości.

int MPI_Type_contiguous(int count, MPI_Datatype oldtype,MPI_Datatype *newtype);

Vector
Elementy są kopiami danego typu wejściowego, ale pomiędzy nimi występują odstępy będące wielokrotnościami rozpiętości tego typu.

int MPI_Type_vector(int count, int block_length,int stride, MPI_Datatype element_type,MPI_Datatype *newtype);

Hvector
Jak Vector ale odstępy między elementami są określone w bajtach.

int MPI_Type_vector(int count, int block_length,int stride, MPI_Datatype element_type,MPI_Datatype *newtype);

Indexed
Jak Vector ale odstępy między elementami są określone dowolnie przez tablicę przesunięć.

int MPI_Type_indexed(int count,int *array_of_block_lengths,int *array_of_displacements,MPI_Datatype element_type,                 MPI_Datatype *newtype);

Hindexed
Jak Indexed ale elementy tablicy przesunięć są podane w bajtach.

int MPI_Type_indexed(int count,int *array_of_block_lengths,int *array_of_displacements,MPI_Datatype element_type,MPI_Datatype *newtype);

Struct
Najbardzięj ogólny typ, elementy nie muszą być jednakowego typu a odstępy między nimi są określone przez tablicę przesunięć, której elementy są podane w bajtach.

int MPI_Type_struct(int count,int *array_of_block_lengths,MPI_Aint *array_of_displacements,MPI_Datatype *array_of_types,MPI_Datatype *newtype);

Extent
Zwraca rozmiar w bajtach danego typu danych. Funkcja ta jest użyteczna w podprogramach, które wymagają rozmiaru danego typu w bajtach np. w celu obliczenia przesunięcia.

int MPI_Type_extent(MPI_Datatype dtype, MPI_Aint *pextent)

Commit
Zatwierdza nowy typ w systemie. Wymagane dla wszystkich nowo stworzonych pochodnych typów danych.

int MPI_Type_commit ( MPI_Datatype *datatype )

Przykład - CONTIGUOUS[edytuj | edytuj kod]

#include "mpi.h"
#include <stdio.h>
#define SIZE 4
main(int argc, char *argv[])
{ int numtasks, rank, source=0, dest, tag=1, i;
float a[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};
float b[SIZE];
MPI_Status stat;
MPI_Datatype rowtype;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
MPI_Type_contiguous(SIZE, MPI_FLOAT, &rowtype);
MPI_Type_commit(&rowtype);
if (numtasks == SIZE){
if (rank == 0) { for (i=0; i<numtasks; i++) MPI_Send(&a[i][0], 1, rowtype, i, tag, MPI_COMM_WORLD); }
MPI_Recv(b, SIZE, MPI_FLOAT, source, tag, MPI_COMM_WORLD, &stat); printf("rank= %d b= %3.1f %3.1f %3.1f %3.1f\n", rank,b[0],b[1],b[2],b[3]); }
else
printf("Must specify %d processors. Terminating.\n",SIZE);
MPI_Finalize();
}


źródło: http://icis.pcz.pl/~roman/mpi-www/

Linki zewnętrzne[edytuj | edytuj kod]