background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

3

 

OBLICZENIA RÓWNOLEGŁE

Temat 6:

Narzędzia i środowiska 

programowania równoległego

Prowadzący:

dr inż. Zbigniew TARAPATA

pok.225, tel.: 83-73-38

e-mail:

Zbigniew.Tarapata@wat.edu.pl

http://

tarapata.

tarapata.

strefa

strefa

.pl

.pl

/

/

p_obliczenia_rownolegle

p_obliczenia_rownolegle

/

/

 

 

 

 

 

 

 

 

 

 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

4

Realizacja modelu równoległości danych – High Performance Fortran. 

 

High Performance Fortran jest rozszerzeniem Fortranu 95 – 

popularnego języka programowania naukowo-inżynierskiego. 

Realizuje on model programowania z równoległością danych, głównie 

dla operacji wektorowych i macierzowych. Zawiera rozszerzenia w 

postaci dyrektyw kompilatora uzupełniających standardowe instrukcje 

Fortranu. Język ten z założenia ma udostępniać model programowania 

praktycznie realizowalny, prostszy niż inne i równie efektywny. 

W celu zrównoleglenia działania poprzez przypisanie danych 

poszczególnym procesom High Performance Fortran udostępnia 

dyrektywy określające ułożenie procesorów: 

 

!HPF$ PROCESSORS, DIMENSION(3,4):: P1 

!HPF$ PROCESSORS, DIMENSION(2,6):: P2 

!HPF$ PROCESSORS, DIMENSION(12):: P3 

!HPF$ PROCESSORS, DIMENSION(2,2,3):: P4 

 

W jednym programie może istnieć wiele zdefiniowanych układów 

procesorów, wykorzystywanych w różnych miejscach do realizacji 

różnych operacji. 

Przypisanie (dystrybucja) danych odbywa się za pomocą 

dyrektywy DISTRIBUTE, np.: 

!HPF$ PROCESSORS, DIMENSION(4):: P 

REAL, DIMENSION(10):: T1, T2 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

5

!HPF$ DISTRIBUTE T1(BLOCK) ONTO P 

!HPF$ DISTRIBUTE T1(CYCLIC) ONTO P 

Określenie BLOCK oznacza podział tablicy na bloki przypisywane 

kolejnym procesorom, określenie CYCLIC oznacza przypisywanie 

kolejnych wyrazów tablicy kolejnym procesorom z okresowym 

zawijaniem. 

Wykonanie równoległe odbywa się poprzez: 

• 

realizację standardowych operacji wektorowych Fortranu 

95 

A+B, A*B, A(B*C) – operacje dozwolone dla macierzy 

zgodnych, tj. mających ten sam kształt, 

• 

pętli operujących na wszystkich elementach macierzy: 

FORALL ( i=1..n, j=1..m, A[i,j].NE.0 ) A(i,j) = 1/A(i,j) 

Równoległość jest uzyskiwana niejawnie – programista nie określa, 

które operacje są wykonywane na konkretnym procesorze. 

 

Unified Parallel C. 

 

Unified Parallel C (UPC) to bardzo wygodne w użyciu rozszerzenie 

języka C służące do programowania równoległego. Oparte jest ono na 

modelu programowania równoległego o pamięci fizycznie 

rozproszonej, logicznie współdzielonej. UPC to niewielkie i bardzo 

wygodne w użyciu narzędzie pozwalające tworzyć wydajne programy. 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

6

Najprostszy program wypisujący komunikaty Hello World z 

każdego procesora (wraz z numerami procesów oraz liczbą tych 

procesów) wygląda następująco: 

 

#include <upc_relaxed.h> 
#include <stdio.h> 
void main(){ 
 

 

 

printf("Hello World from THREAD %d (of %d 

THREADS)\n",MYTHREAD,THREADS); 

Każdy procesor (tutaj nazywany wątkiem - THREAD) uruchamia 

równolegle własną kopię powyższego programu. Pierwsza linia kodu, 

poprzez dołączenie pliku nagłówkowego  upc_relaxed.h, 

specyfikuje sposób korzystania z pamięci. W UPC sposoby obsługi 

pamięci są dwa:  

• 

upc_relaxed.h - procesy mogą odczytywać zmienne 

współdzielone dowolnie w każdej chwili. Korzystanie z tego 

sposobu jest preferowane, gdyż umożliwia kompilatorom lepszą 

optymalizację.  

• 

upc_strict.h - współdzielone zmienne i dane 

synchronizowane są za każdym razem przed dostępem do nich. 

Oznacza to, że jeśli współdzielona zmienna jest aktualnie 

modyfikowana przez jeden proces, pozostałe procesy będą 

czekać aż do synchronizacji przed odczytaniem jej wartości.  

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

7

Dalsza część powyższego programu jest zwykłym kodem języka C. 

Do wypisania komunikatu używamy funkcji printf. Korzystamy 

jednak z dwóch zmiennych dostępnych dzięki załączeniu pliku 

nagłówkowego UPC:  

• 

THREADS - oznacza liczbę procesów, które uczestniczą w 

aktualnym uruchomieniu programu, 

• 

MYTHREAD - oznacza numer procesu aktualnie uruchomionego. 

Dla lepszego wyjaśnienia znaczenia zmiennych THREADS i 

MYTHREAD prześledźmy następujący przykład:  

#include <upc_relaxed.h> 
#include <stdio.h> 

 

void main(){ 
   if(MYTHREAD==0){ 
      printf("Starting execution at THREAD %d\n",MYTHREAD); 
   } 
 

 

 

printf("Hello World from THREAD %d (of %d 

THREADS)\n",MYTHREAD,THREADS); 

Program ten jest bardzo podobny do wcześniejszego, z tą różnicą, 

że warunek zawarty w instrukcji if specyfikuje, iż proces o numerze 

0 dodatkowo wywoła funkcję  printf wypisując na standardowe 

wyjście komunikat "Starting execution at THREAD 0".  

 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

8

Równoległe pętle for - upc_forall. 

W UPC mamy możliwość implementacji pętli, których kolejne 

iteracje uruchamiane będą przez różne procesy. Struktura wywołania 

takiej pętli jest następująca: 

upc_forall( wyrażenie; wyrażenie; wyrażenie; wskazanie) 

Czwarty parametr pętli  upc_forall może być albo liczbą 

naturalną  tłumaczoną na (liczba % THREADS); lub adresem 

który wskazuje na konkretny proces do którego adres ten jest 

przypisany. Oto dwa krótkie przykłady użycia upc_forall:  

upc_forall(i=0;i<N;i++;i) { 
   printf("THREAD  %d  (of  %d THREADS) performing iteration 
%d\n",MYTHREAD,THREADS,i); 

Powyższa pętla wypisze numery procesów wraz z liczbą 

reprezentującą numer iteracji przez te procesy wykonywanej. Iteracja 

o numerze i  będzie wykonywana przez proces o numerze i % 

THREADS.  

 

 

 

 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

9

W drugim przykładzie widzimy, iż proces który ma wykonywać 

daną iterację może zostać również wskazany przez adres pamięci do 

niego przydzielony:  

1:      #include <upc_relaxed.h> 
2:      #include <stdio.h> 
3:      #define N 10 
4:      shared [2] int arr[10]; 
5: 
6:      int main() { 
7:              int i=0; 
8:              upc_forall(i=0;i<N;i++;&arr[i]) { 
9:                 printf("THREAD %d (of %d THREADS) performing 
iteration %d\n",MYTHREAD,THREADS,i); 
10:             } 
11:             return 0; 
12:     } 

 

Linijka:  

4:      shared [2] int arr[10]; 

definiuje tablicę liczb typu integer o wymiarze 10. Pamięć ta 

rozdzielona jest pomiędzy procesy, po 2 elementy tablicy dla każdego 

procesu cyklicznie. W deklaracji tej tablicy kluczowe jest użycie 

słówka  shared (opisanego poniżej). Czwarty parametr 

pętli upc_forall to adres kolejnych elementów tablicy arr. Adres 

ten tłumaczony jest na identyfikator procesu któremu dany adres jest 

przypisany. Wybrany proces realizuje następnie kod przypisany dla 

danej iteracji. Oznacza to, że iteracja i wykonywana jest przez proces 

do którego przypisany jest adres &arr[i]. 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

10

Zmienne współdzielone - wyrażenie shared. 

Aby zrozumieć wszystkie oznaczenia związane z wyrażeniem 

shared należy skomentować krótko pomysł, który kryje się za 

modelem pamięci fizycznie rozproszonej, logicznie współdzielonej. 

Otóż przestrzeń pamięciowa w UPC dzieli się na prywatną i 

współdzieloną. Każdy proces ma swoją  własną przestrzeń prywatną 

oraz porcję przestrzeni współdzielonej. Prywatna pamięć obsługiwana 

jest tak jak w zwykłym języku C. Cała przestrzeń współdzielona 

podzielona jest na części, z których każda przypisana jest logicznie do 

przestrzeni pamięciowej jednego z procesów. Widać to na poniższym 

rysunku:  

 

Rysunek A.1. Pamięć współdzielona i prywatna. 

 

Tablice współdzielone logicznie rozdystrybuowane w blokach po 

procesorach to tablice widziane przez wszystkie procesy, natomiast 

fizycznie przechowywane w pamięci przypisanej do jednego z 

procesów. Aby zdefiniować współdzieloną pamięć musimy użyć 

słówka kluczowego shared. Mamy do dyspozycji następujące 

kombinacje:  

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

11

1:     int local_counter;                  //prywatna zmienna  
2:     shared int global_counter;          
//współdzielona zmienna 
3:     shared int array1[N];               
//współdzielona tablica 
4:     shared [N/THREADS] int array2[N];   
//współdzielona tablica 
5:     shared [] int array3[N];            
//współdzielona tablica 
6:     shared int *ptr_a
;           //prywatny wskaźnik do współdzialonej 
pamięci 
7:     shared int *shared ptr_c
;           //współdzielony wskaźnik do 
współdzielonej pamięci 

Komentarza wymaga różnica pomiędzy trzema zdefiniowanymi 

tablicami  array1, array2, array3. Zwykle polecenie 

shared ma następującą formę:  

shared [rozmiar_bloku] typ nazwa_zmiennej 

Powyższy zapis należy czytać w następujący sposób: zmienna o 

nazwie  nazwa_zmiennej o typie typ jest współdzielona 

pomiędzy wszystkimi procesami i zostaje rozdystrybuowana 

cyklicznie pomiędzy wszystkimi procesami w blokach wielkości 

rozmiar_bloku. Jeśli wielkość bloku nie jest podana wówczas 

przyjmowana jest wielkość 1. Nieskończoną wielkość bloku 

oznaczamy []. Na kolejnych trzech rysunkach pokazany jest sposób 

rozdystrybuowania pamięci pomiędzy procesami dla tablic array1, 

array2, array3. 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

12

 

Rysunek A.2. Rozłożenie elementów tablicy array1 po procesach z 

rozmiarem bloku równym 1 dla N=9 oraz THREADS=3. 

 

 

Rysunek A.3. Rozłożenie elementów tablicy array2 po procesach z 

rozmiarem bloku równym N/THREADS dla N=10 oraz 

THREADS=3. 

 

 

 

Rysunek A.4. Rozłożenie elementów tablicy array3 po procesach z 

nieskończonym rozmiarem bloku [] dla N=10 oraz THREADS=3. 

 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

13

Synchronizacja procesów i dostępu do pamięci w UPC. 

 

Bariery upc_barrier. 

Polecenie  upc_barrier  służy do synchronizacji procesów. 

Umieszczenie go w kodzie oznacza tak naprawdę postawienie bariery 

w kodzie, tzn. żaden proces nie ma prawa przekroczyć bariery (linii w 

kodzie) dopóki wszystkie procesy do niej nie dotrą. Bariery 

wykorzystywane są najczęściej gdy występuje zależność danych 

pomiędzy procesami. Oto prosty przykład: 

1:      #include <upc_relaxed.h> 
2:      #include <stdio.h> 
3: 
4:      shared int a=0; 
5:      int b; 
6: 
7:      int computation(int temp) { 
8:              return temp+5; 
9:      } 
10: 
11:     int main(){ 
12:             int result=0, i=0; 
13:             do { 
14:                     if(MYTHREAD==0) { 
15:                             result=computation(a); 
16:                             a=result*THREADS; 
17:                     } 
18:                     upc_barrier; 
19:                     b=a; 
20:                     printf("THREAD %d: b=%d\n",MYTHREAD,b); 
21:                     i++; 
22:             } while(i<4); 
23:             return 0; 
24:     } 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

14

W linii 16 powyższego kodu procesor o numerze 0 uaktualnia 

wartość zmiennej a, współdzielonej przez wszystkie procesy. W linii 

19 wszystkie procesy dokonują uaktualnienia swojej prywatnej 

zmiennej b przypisując im aktualną wartość zmiennej a. Zauważmy, 

że gdybyśmy nie umieścili bariery w linii 18 wówczas nie mielibyśmy 

gwarancji,  że wszystkie prywatne zmienne b zostały uaktualnione 

najnowszą wartością zmiennej a.  

Blokowanie dostępu do zmiennej - upc_lock/upc_unlock. 

Dzięki użyciu blokad upc_lock/upc_unlock możemy 

uzyskać gwarancję,  że pewna zmienna nie będzie czytana przez 

proces jeśli jest w danej chwili uaktualniana przez inny proces. 

Przykład:  

Zapoznamy się teraz z implementacją algorytmu obliczającego 

liczbę Pi ze wzoru: 

+

=

Π

1

0

2

1

1

4

x

 i całkowania numerycznego metodą 

prostokątów.  

Wersja szeregowa takiego programu wygląda następująco:  

 

 

 

 

 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

15

//Przyklad szeregowy - calkowanie numeryczne 

//Obliczanie liczby Pi 

#include<math.h> 

#define N 1000000 

#define f(x) (1.0/(1.0+x*x)) 

float pi=0.0; 

void main(void) 

        int i; 

 

        for(i=0;i<N;i++) 

                pi+=(float) f( (0.5+i)/(N) ); 

        pi*=(float)(4.0/N); 

 

        printf("PI=%f\n",pi); 

 

 

 

 

 

 

 

 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

16

Wersja równoległa zaimplementowana przy pomocy UPC wygląda 

tak:  

//Przyklad UPC - calkowanie numeryczne 
//
Obliczanie liczby Pi 
#include<upc_relaxed.h> 
#include<math.h> 
#define N 1000000 
#define f(x) (1.0/(1.0+x*x)) 
 
upc_lock_t *l; 
shared float pi=0.0; 
void main(void) 

        float local_pi=0.0; 
        int i; 
        l=upc_all_lock_alloc(); 
 
        upc_forall(i=0;i<N;i++;i) 
                local_pi+=(float) f( (0.5+i)/(N) ); 
        local_pi*=(float)(4.0/N); 
 
        upc_lock(l); 
        pi+=local_pi; 
        upc_unlock(l); 
 
        upc_barrier; 
        if(MYTHREAD==0) printf("PI=%f\n",pi); 
        if(MYTHREAD==0) upc_lock_free(l); 

Przykład ten jest bardzo fajny, gdyż widać tutaj zarówno użycie 

bariery  upc_barrier jak i zabezpieczenia upc_lock . Przed 

równoczesnym dostępem z więcej niż jednego procesora 

zabezpieczona jest współdzielona zmienna pi.  

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

17

Dostępność UPC, wspierane platformy. 

UPC jest wspierane przez kompilatory na niektórych komputerach 

firmy Cray ale również HP, SGI czy Sun. Dostępne są również pewne 

darmowe implementacje, dokumentacje i przykłady:  

• 

Michigan Technological University (http://www.upc.mtu.edu/)  

• 

George Washington University (http://www.gwu.edu/~upc/)  

• 

Berkeley Unified Parallel C (UPC) Project (http://upc.lbl.gov/). 

OpenMP. 

OpenMP jest to API pozwalające tworzyć kod wykonywany 

równolegle w maszynach z pamięcią współdzieloną. Jest to zbiór 

rozszerzeń do języków C, C++ i FORTRAN. Składa się ono z trzech 

elementów: 

• 

Dyrektyw preprocesora 

• 

Funkcji bibliotecznych 

• 

Zmiennych środowiskowych 

Dzięki temu API programiści są w stanie tworzyć aplikacje, 

których kod może być wykonywany przez wiele wątków równolegle. 

Jak zobaczymy dalej, nie jest konieczna bezpośrednia obsługa wątków 

a OpenMP jest niezależne od systemu, na którym kompilujemy kod. 

OpenMP jest powszechnie przyjętym standardem i wynikiem wielu 

lat badań nad tworzeniem programów wykonywanych równolegle. 

Wersja standardu 1.0 ukazała się dla języka FORTRAN w 1997 roku, 

a już w 1998 dostępna była dla C/C++. W Visual C++ 2005 do 

czynienia mamy z wersją OpenMP 2.0, która ukazała się w 2002 roku. 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

18

OpenMP działa na maszynach z pamięcią współdzieloną. Oznacza 

to, że wszystkie procesory mają dostęp przynajmniej do fragmentów 

pamięci wpólnej. Dodatkowo system działa na jednej kopii systemu 

operacyjnego a komunikacja procesorów polega na zapisie i odczycie 

ze wspomnianej wcześniej wspólnej pamięci. Warunki te spełnia 

idealnie większość dostępnych obecnie komputerów w tym również 

dobrze nam znane komputery klasy PC. 

Gdzie zatem warto użyć OpenMP? Zdecydowanie przeznaczone 

ono jest do systemów, które potrafią wykonywać kod w wielu 

wątkach równolegle oraz wymagają sporej mocy obliczniowej. Mogą 

być to aplikacje począwszy od gier, poprzez obróbkę  dźwięku i 

obrazu a skończywszy na obliczeniach naukowych. Mówi się,  że 

znajduje ono zastosowanie w małych i średnich aplikacjach 

wykonujących intensywne obliczenia a najlepiej jeżeli obliczenia te 

wykonywane są w pętlach. Dyrektywy OpenMP bazują na dyrektywie 

preprocesora #pragma w C/C++: 

 

#pragma omp dyrektywa [wyrażenie [wyrażenie]…] 

  

Najbardziej podstawowe pragmy omp to parallel,  for,  sections i 

section, np.: 

#pragma omp parallel 

#pragma omp for 

#pragma omp sections 

#pragma omp section 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

19

  We wstępie przyjrzyjmy się następującej pętli for: 

 

void main() 

      double Table[1000]; 
  
      for(int i=0;i<1000;i++)  
            DoSomething(Table[i]); 

może zostać zrównoleglona w następujący sposób: 

void main() 

      double Table[1000]; 
  
      #pragma omp parallel for 
      for(int i=0;i<1000;i++)  
            DoSomething(Table[i]); 

 

Zaledwie jedna linia kodu, a dysponując odpowiednim 

kompilatorem i opowiednim sprzętem możemy powyższą  pętlę 

wykonywać równolegle przez wiele wątków. 

 

Wątki a OpenMP 

Równoległość wykonywania kodu z wykorzystaniem opisywanego 

standardu polega na wykorzystaniu wątków. OpenMP to standard, 

który ma być niezależny od platformy, został on zatem 

zaprojektowany tak, iż programista nie ma bezpośrednio do czynienia 

z tworzeniem, synchronizacją i niszczeniem wątków 

charakterystycznych dla danej platformy. Za pomocą pragm OpenMP 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

20

steruje się ilością tworzonych wątków, synchronizacją oraz dostępem 

do danych. Działanie programu stworzonego za pomocą tego 

standardu dobrze zilustrować może następujący rysunek. 

 

Rysunek A.5. Wątki w OpenMP. 

 
OpenMP działa na zasadzie fork-join. Na samym początku program 

startuje z jednym wątkiem, zwanym wątkiem głównym (ang. master 

thread). Tuż po starcie znajdujemy się w sekwencyjnym regionie 

kodu. W momencie, gdy wejdziemy w region równoległy, zostaje 

stworzona odpowiednia pula wątków, do każdego wątku przypisana 

zostają odpowiednie dane odpowiednio zainicjalizowane. Każdy 

wątek ma określony kod do wykonania. Następuje współbieżne 

wykonanie kodu poszczególnych wątków (czyli następuje 

rozwidlenie, z ang. fork). Gdy wątek kończy swoją pracę może lub nie 

wymagać synchronizacji z pozostałymi wątkami. Po zakończniu pracy 

wszystkich wątków dane zostają zsynchronizowane do wątku 

głównego i następuje wykonanie kolejnego regionu sekwencyjnego 

(następuje „połączenie” wątków, z ang. join). 

  

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

21

Wszystkie elementy a mianowicie ilość  wątków, kod, zmienne 

przypisane do wątków, sposoby synchronizacji określa programista za 

pomocą dyrektyw, modyfikatorów dyrektyw i funkcji czasu 

wykonania. 

 

PVM (Parallel Virtual Machine) 

W skład oprogramowania wchodzi kilka programów i biblioteka 

podprogramów pozwalająca na tworzenie aplikacji działających 

równolegle na kilku komputerach (nawet o różnych architekturach). 

Programista dostaje do ręki narzędzia pozwalające na pisanie 

aplikacji, które dzielą problem obliczeniowy na kilka (najczęściej 

identycznych, ale mniejszych) zadań, przekazywanie im danych i 

synchronizacje ich wykonania.  

Wykorzystanie możliwości systemu wymaga (najczęściej) takiego 

zaprojektowania algorytmu zadania, aby łatwo dzieliło się na 

fragmenty możliwe do równoległego wykonania. Nie mogą to być 

zadania zbyt małe, a wymagające przekazywania znacznych ilości 

danych gdyż wówczas synchronizacja zadań zużywa znaczną część 

przepustowości sieci. 

Zaletą systemu jest to, że dostępny jest na wszystkich praktycznie 

platformach sprzętowych dostępnych na uczelni (SUN, SGI, IBM, 

HP, komputery PC pracujące pod Linuxem). 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

22

PVM tworzony był na University of Tennessee, The Oak Ridge 

National Laboratory i Emory University. Jego pierwsza wersja została 

napisana w ORNL w 1989 roku. Następnie przepisano ją zupełnie w 

University of Tennessee i udostępniono jako wersję drugą w marcu 

1991. Wersja trzecia ukazała się w 1993 roku. 

Dzisiaj PVM nie jest już aktywnym projektem, ale nadal stosuje się 

go w programowaniu równoległym. 

Opis działania PVM 

PVM jest narzędziem służącym głównie do pośrednictwa w 

wymianie informacji pomiędzy procesami uruchomionymi na 

oddzielnych maszynach, oraz do zarządzania nimi za pośrednictwem 

konsoli pvm. Z punktu widzenia obecnej definicji maszyny wirtualnej, 

pvm w sumie nie zasługuje na swoją nazwę, powinien się bardziej 

nazywać "zarządca procesów zrównoleglonych", jednakże historia 

pvm sięga dawnych czasów, kiedy to nazewnictwo było trochę inaczej 

rozumiane. 

W dużym uproszczeniu PVM pozwala na "połączenie" większej 

ilości komputerów w jeden. Odbywa się to na zasadzie dołączania 

kolejnych hostów (komputerów), na których został zainstalowany i 

skonfigurowany pvm. Podłączanie hosta jest w uproszczeniu 

procedurą połączenia przez rsh, uruchomieniu demona pvm, i 

dostarczenia mu informacji o tym kto jest jego "rodzicem" oraz o 

parametrach istniejącej sieci. 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

23

W momencie kiedy cała "maszyna wirtualna" już jest 

skonfigurowana, rola pvm sprowadza się do stanowienia pomostu 

wymiany informacji pomiędzy procesami, które się w pvm 

„zarejestrują” oraz umożliwienia administrowana stanem 

zarejestrowanych w pvm procesów. Z takiego punktu widzenia pvm 

można nazwać raczej „demonem komunikacji międzyprocesowej” i 

można to podeprzeć najprostszym argumentem, mianowicie pvm nie 

stanowi interfejsu do systemu operacyjnego lub maszyny na której 

działa dany proces. 

PVM pozwala zautomatyzować proces uruchamiania aplikacji na 

innych komputerach. Jeżeli uruchomimy z poziomu normalnej konsoli 

program, który następnie zarejestruje się w pvm i użyje funkcji 

pvm_spawn(), aby uruchomić dolną ilość procesów, to już rolą PVM 

będzie zatroszczenie się, gdzie zostaną one uruchomione, oraz samo 

ich uruchomienie (możliwe jest podanie parametru uściślającego 

lokalizacje docelową uruchomienia). W przypadkach obliczeń, gdzie 

procesy-„dzieci”, rodzą się i umierają w bliżej nieokreślonych 

interwałach i te zjawiska się przeplatają, zautomatyzowanie 

dostarczane przez PVM okazuje się bardzo pomocne, ponieważ 

"odciąża" programistę z potrzeby troski o prawidłowe rozłożenie 

obliczeń na hosty. 

Identyfikacja w pvm odbywa się przez oddzielne numery 

identyfikacyjne przydzielane procesom w momencie rejestrowania się 

w pvm. Numery te stanowią zupełną abstrakcję od systemu 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

24

operacyjnego i sprzętu, na którym uruchomiony jest dany proces. 

Numer identyfikacyjny jest bardzo istotnym elementem pvm, 

ponieważ stanowi on niejako adres danego procesu i jest on 

niepowtarzalny w zakresie pojedynczej maszyny pvm. Adres jest 

wykorzystywany przy wszystkich procedurach dotyczących innych 

procesów, można przesłać informacje pod dany adres, można „zabić” 

zdany adres i wiele innych. 

Pojęcia „przyłączenie się do pvm” czy też „zarejestrowanie się w 

pvm” znaczą dokładnie to samo i oznaczają wywołanie po raz 

pierwszy jakiejkolwiek procedury pvm, przez co demon pvm 

przydziela danemu procesowi numer identyfikacyjny i zapisuje 

informacje o nim w swojej bazie procesów. 

Podstawowe cechy środowiska PVM 

- zarządzanie zasobami, 

- możliwość dodania/usunięcia węzła z komputera wirtualnego, 

- zarządzanie procesami, 

- dynamiczne uruchamianie/usuwanie procesów, 

- komunikacja między procesami na zasadzie wymiany komunikatów 

(ang. message passing), 

- niezawieszające wysłanie informacji, zawieszające i niezawieszające 

odebranie informacji, grupowe operacje komunikacyjne, 

- możliwość dynamicznego tworzenia grup procesów (zadań) - 

dowolne zadanie może przyłączyć się do grupy lub porzucić ją w 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

25

dowolnym momencie czasu, 

- tolerowanie defektów - komputer wirtualny automatycznie wykrywa 

defekty, co daje możliwość dostosowania jego funkcjonowania do 

aktualnej konfiguracji. 

Przykłady dziedzin, w których wykorzystywany jest PVM 

- prognoza pogody i modelowanie klimatu, 

- biologia molekularna, 

- modelowanie procesów spalania, 

- rzeczywistość wirtualna, 

- chemia kwantowa, 

- rozpoznawanie obrazów, 

- dynamika płynów, 

- algebra liniowa, 

- metoda Monte Carlo, 

- metoda elementów skończonych, 

- optymalizacja kombinatoryczna, 

- modelowanie struktury białka, 

- modelowanie sekwencji DNA. 

Przesyłanie informacji pomiędzy programami. 

W pvm wyróżniamy dwa rodzaje wysyłania informacji: blokujące i 

nieblokujące. Przesyłanie blokujące to takie, które blokuje program 

wysyłający na procedurze wysłania do momentu aż program 

odbierający wywoła procedurę otrzymywania informacji i na odwrót, 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

26

blokuje program odbierający na procedurze odbierania do momentu, 

aż zostaną mu wysłane jakieś informacje przez program nadający. 

Przesyłanie nieblokujące - jak wynika z nazwy - jest pozbawione tych 

negatywnych czynników, jednakże wymaga ono nieraz wsparcia przez 

„procesy zewnętrzne (proces pvmmsg)”, oraz niejednokrotnie 

wymaga synchronizacji procesów w celu ich skomunikowania. 

Dlatego dla prostych programów przeważnie wykorzystuje się 

przesyłanie blokujące. 

Do przesyłania informacji w pvm stosuje się "bufory". Każdy 

proces który zarejestruje się w pvm wywołując dowolną jego 

procedurę (najczęściej stosuje się wywołanie pvm_mytid()), zostaje 

wyposażony w jeden bufor nadawczy i jeden bufor odbiorczy. 

Możliwe jest użycie buforów innych niż domyślne, jednakże wymaga 

to ich utworzenia. 

Procedura przesyłania dzieli się na: 

1. Wysłanie informacji przez program „nadający”  

1. inicjację bufora wysyłania 

2. spakowanie danych do bufora 

3. wysłanie wiadomości 

2. Odebranie informacji przez programy „odbierające”  

1. odebranie wiadomości 

2. rozpakowanie informacji z bufora 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

27

Do bufora można spakować więcej niż jedną informację w celu 

wysłania ich większej ilości na raz, jednakże należy pamiętać o ich 

rozpakowaniu z bufora odbiorczego w odpowiedniej kolejności. 

Przykładowe procedury przesyłania informacji. 

Komunikacja punkt-punkt. 

pvm_send 

 int retval = pvm_send(int tid ,int msgtag); 

Parametry 

• 

tid - identyfikator procesu ,do którego ma zostać wysłany 

komunikat. 

• 

msgtag - liczba całkowita, definiowana przez użytkownika jako 

etykieta komunikatu (powinna być >=0). 

• 

retval - kod statusu zwracany przez funkcję (retval < 0 oznacza 

błąd podczas wykonania operacji). 

pvm_recv 

 int retval = pvm_recv(int tid ,int msgtag); 

Parametry 

• 

tid - identyfikator procesu ,od którego ma zostać odebrany 

komunikat. 

• 

msgtag - liczba całkowita, definiowana przez użytkownika jako 

etykieta komunikatu (powinna być >=0). 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

28

• 

retval - kod statusu zwracany przez funkcję (retval < 0 oznacza 

błąd podczas wykonania operacji). 

Komunikacja grupowa 

pvm_mcast 

int retval = pvm_mcast( int *tids, int ntask, int msgtag ); 

Parametry 

• 

tids - tablica liczb całkowitych o ntask elementach 

zawierających identyfikatory zadań procesów, do których ma 

zostać wysłany komunikat. 

• 

ntask - ilość procesów, do których będzie wysłany komunikat. 

• 

msgtag - liczba całkowita, definiowana przez użytkownika jako 

etykieta komunikatu (powinna być >=0). 

• 

retval - kod statusu zwracany przez funkcję (retval < 0 oznacza 

błąd podczas wykonania operacji). 

Funkcja wysyła komunikat asynchronicznie do wszystkich 

procesów, których identyfikatory zadań są zapamiętane w tablicy tids 

z wyjątkiem procesu wysyłającego. Etykieta msgtag jest 

wprowadzona dla rozróżniania przesyłanych komunikatów. 

Proces odbierający komunikat może zrobić to albo funkcją 

pvm_recv(), albo pvm_nrecv(). 

 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

29

MPI (Message Passing Interface) 

MPI (ang. Message Passing Interface, Interfejs Transmisji 

Wiadomości) - standard przesyłania komunikatów pomiędzy 

procesami programów równoległych działających na jednym lub 

więcej komputerach. Pierwsza wersja standardu ukazała się w maju 

1994 r. Standard MPI implementowany jest najczęściej w postaci 

bibliotek, z których można korzystać w programach tworzonych w 

różnych językach programowania, np. Fortran, C, C++, Ada. 

Opis 

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. 

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 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

30

pozwala na jego zastosowanie zarówno w komputerach równoległych, 

jak i heterogenicznych sieciach stacji roboczych. 

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. 

Główne własności MPI 

• 

umożliwia efektywną komunikację bez obciążania procesora 

operacjami kopiowania pamięci, 

• 

udostępnia funkcje dla języków C/C++, Fortan oara 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. 

 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

31

Zalety MPI 

• 

dobra efektywność w systemach wieloprocesorowych, 

• 

dobra dokumentacja, 

• 

bogata biblioteka funkcji, 

• 

jest to program public-domain, 

• 

przyjął się jako standard. 

Wady MPI 

• 

statyczna konfiguracja jednostek przetwarzających, 

• 

statyczna struktura procesów w trakcie realizacji programu, 

• 

brak wielowątkowości. 

 

Porównanie PVM i MPI. 

PVM - standard de facto środowiska do przetwarzania 

równoległego i rozproszonego w heterogenicznych sieciach maszyn 

sekwencyjnych, równoległych i wektorowych. 

MPI - standard biblioteki funkcji opartej na modelu obliczeń 

równoległych z wymianą komunikatów. 

Osiągnięcie maksymalnej wydajności to główny priorytet 

przestrzegany przy opracowaniu standardu MPI i jego 

implementacjach dla systemów masywnie równoległych - MPP; 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

32

dlatego dość powszechnie oczekuje się,  że MPI będzie w stanie 

zapewnić większą wydajność niż PVM dla architektur MPP. 

Przenośność a interoperacyjność. 

- oba środowiska zapewniają przenośność czyli możliwość realizacji 

na różnych platformach, 

- tylko PVM zapewnia interoperacyjność czyli możliwość 

komunikowania się i współpracy różnych platform, 

- zapewnienie interoperacyjności odbija się na wydajności, 

- interoperacyjność PVM dotyczy również języków programowania - 

bezproblemowe komunikowanie się programów napisanych w 

językach C (C++) i Fortran. 

PVM opiera się na koncepcji maszyny wirtualnej, która może być 

traktowana jako rozproszony system operacyjny, umożliwiający 

integrację różnych stacji roboczych, komputerów osobistych i MPP w 

jedną całość: 

- pozwala to realizować funkcje dynamicznego zarządzania 

procesami, 

- dopiero standard MPI-2 przewiduje taką możliwość, 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

33

- PVM dostarcza bogate możliwości w zakresie dynamicznego 

zarządzania zasobami (dołączanie / odłączanie węzłów), podczas gdy 

MPI został zaprojektowany z myślą o środowisku statycznym. 

Nie przewidując tworzenia maszyny wirtualnej, standard MPI 

realizuje koncepcję topologii wirtualnych, pozwalających 

użytkownikowi - programiście w łatwy sposób opisywać strukturę 

komunikowania się procesów aplikacji. 

W odróżnieniu od MPI, środowisko PVM zapewnia bazowe 

mechanizmy tolerowania defektów. 

 

TotalView - debugger do programów równoległych. 

 

Uruchomianie programów - znajdywanie i usuwanie błędów - jest 

pracochłonne oraz wymagające dużej ilości czasu. Dobre narzędzia 

programistyczne pozwalają uprościć i skrócić ten proces. Dlatego 

obecnie stosowane środowiska do tworzenia aplikacji sekwencyjnych, 

jak np. MS Visual Studio, czy Borland JBuilder posiadają 

zintegrowany debugger. 

Programy równoległe cechuje większa złożoność i podatność na 

błędy. Niestety, brak jest tu wciąż standardowych pakietów 

oferujących zintegrowane środowisko podobne do wyżej 

wymienionych. Dlatego stworzono osobny i niezależny debugger, 

pozwalający sprawnie uruchamiać programy równoległe. Totalview 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

34

jest najpopularniejszym i jednym z najlepszych programów tego typu. 

Jest dostępny na wiele platform systemowych. Można go używać do 

uruchamiania programów stworzonych przy pomocy różnych technik 

programowania równoległego. 

 

Aplikacja TotalView jest interakcyjnym i symbolicznym 

debuggerem. Pozwala na śledzenie wykonywania programu na 

poziomie kodu źródłowego, zatrzymywanie testowanego programu 

(poprzez określenie punktów kontrolnych) oraz sprawdzanie wartości 

zdefiniowanych zmiennych. W każdej chwili użytkownik może 

obserwować interesujące go zmienne, moduły i etykiety. Aplikacja 

TotalView używana jest w programie wykonywalnym, 

skompilowanym za pomocą kompilatorów CAL (Cray Assembly 

Language), C++, C, Fortran 77, Fortran 90 lub Pascal. 

 

Uruchomienie. 

Użytkownik musi zalogować się na SR2201. Istnieje kilka metod 

wystartowania debuggera  

•  totalview program - działa jak w przypadku klasycznego 

debuggera. Uruchamia program (jeden proces) i przejmuje 

nad nim kontrolę. 

•  totalview - uruchamia sam debugger. Korzystając z listy 

działających procesów można przejąć kontrolę nad dowolnym 

z nich. Możliwość ta jest zresztą zawsze dostępna. 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

35

•  totalview core program - do analizy core file 
•  mpirun <parametry dla MPI> -tv program - inicjalizuje 

środowisko MPI, uruchamia równoległe aplikacje i 

przekazuje je pod kontrolę Totalview, pozwala zatrzymać 

procesy przy MPI_Init() 

•  totalview -pvm - opcja pozwalająca na współpracę z PVM 

Obsługa. 

Totalview to program z graficznym interfejsem użytkownika. Jego 

obsługa sprowadza się do przełączania pomiędzy oknami, 

wskazywania myszą na miejsca w kodzie programu, wyboru opcji z 

menu i obsługi okien dialogowych. Zaawansowani użytkownicy mogą 

oczywiście używać skrótów klawiszowych. 

Totalview może naraz kontrolować wiele programów, każdy 

program może składać się z wielu procesów, każdy proces może mieć 

wiele wątków. Osobne okno pokazuje listę procesów, nad którymi 

Totalview przejął kontrolę. Użytkownik może otworzyć osobne okno 

do  śledzenia każdego procesu, może też  użyć jednego okna i 

przełączać się pomiędzy procesami poprzez kliknięcie na odpowiedni 

przycisk.  

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

36

 

Rysunek A.6. Wygląd TotalView 

 

 

 

 

 

 

 

background image

Obliczenia równoległe  

dr inż. Zbigniew Tarapata 

Temat nr 6: Narzędzia i środowiska programowania równoległego 

 

 

37

Możliwości. 

Debugger wyświetla tekst programu (C, C++, Fortran). Klikając na 

wybrane miejsca w programie można ustawić pułapki, wyświetlić 

zawartość zmiennej itp. Można przełączyć się na poziom asemblera. 

Debugger rozpoznaje typ zmiennych i pokazuje ich wartość w 

odpowiedni sposób. Dla zmiennych złożonych (rekordy, tablice) 

można  żądać wyświetlenia wartości składowych przez kliknięcie na 

nie. Wartości wybranych zmiennych można monitorować w sposób 

ciągły. Stos procesu (zagnieżdżone wywołania funkcji) jest 

uwidoczniony. 

Wstawione w kod pułapki mogą dotyczyć pojedynczego procesu, 

lub wszystkich procesów wchodzących w skład aplikacji (istotne dla 

programów SPMD). Ta druga opcja bardzo ułatwia uruchamianie 

programów MPI. 

Wbudowany interpreter C, C++, Fortanu pozwala na dołączanie 

dodatkowego kodu i jego modyfikacje w trakcie działania programu. 

W połączeniu ze specjalnymi instrukcjami w Totalview uzyskujemy 

możliwość tworzenia pułapek warunkowych.