Co-Array Fortran

(Przekierowano z CAF)
Dostęp do zasobów ICM
Konto użytkownika
Grant obliczeniowy
Uruchamianie obliczeń
Oprogramowanie
Pomoc
Poradniki różne

Spis treści

Wstęp

Co-Array Fortran (CAF) jest jednym z prostszych i efektywniejszych narzędzi do zrównoleglania kodu. Jest to niewielkie, a dzięki temu również łatwe do opanowania, rozszerzenie standardu F95. Mimo stosunkowo małej liczby nowych cech, CAF pozwala na bardzo intuicyjne i łatwe tworzenie wydajnych programów. Dlatego też dokładniejszą prezentację technik zrównoleglania kodu rozpoczynamy właśnie od tego narzędzia.

Do podziału pracy między różne CPU służy w CAF kilka nowych w porównaniu ze standardem F95 funkcji wbudowanych (tzw. intrinsic) i możliwość deklarowania tzw. co-arrays (współtablic). Najważniejsze elementy niezbędne do pisania/zrównoleglania prostych programów zostaną dokładniej opisane poniżej.

Który mamy procesor? Funkcje THIS_IMAGE() i NUM_IMAGES()

Aby zrozumieć działanie funkcji THIS_IMAGE() i NUM_IMAGES() należy uświadomić sobie, że w filozofii CAF każdy z wykorzystanych procesorów wykonuje kopię dokładnie tego samego programu. Jeżeli więc mamy w jakiś sposób podzielić pracę między różne CPU musimy mieć możliwość odczytania, numeru procesora na którym nasz program pracuje. Służą do tego właśnie funkcje THIS_IMAGE() i NUM_IMAGES(). Zwracają one liczbę typu INTEGER odpowiednio z numerem CPU (lub inaczej numerem obrazu, jak to się przyjęło nazywać w terminologii CAF) i liczbą wszystkich dostępnych obrazów.

Np. prosty programik przykładowy:

 PROGRAM WITAJ
      WRITE (*,*) 'Witaj swiecie od procesora ', THIS_IMAGE(), ' z ', NUM_IMAGES(),'!' 
 END

po uruchomieniu na trzech procesorach da w wyniku:

 Witaj swiecie od procesora  1  z  3 !
 Witaj swiecie od procesora  3  z  3 !
 Witaj swiecie od procesora  2  z  3 !                                                                       

Warto zauważyć, że kolejność pojawiania się napisów jest losowa i może być inna dla każdego uruchomienia programu.

Co to są współtablice (co-arrays)? Komunikacja

Możliwość wykorzystania różnych procesorów do różnych celów w zależności od wartości zwróconej przez funkcję THIS_IMAGE() to jeszcze nie wszystko. Do efektywnego pisania równoległych programów potrzebujemy dodatkowo możliwości wymiany danych między obrazami.

W CAF każdy procesor ma do dyspozycji własne "prywatne kopie" wszystkich zmiennych. Znaczy to, że zmiana wartości pewnej zmiennej A na jednym z procesorów NIE będzie widoczna dla pozostałych CPU. Zmienne, których wartości chcemy udostępnić innym procesorom w CAF powinny być deklarowane jako tzw. współtablice (co-arrays).

Robi się to w następujący sposób:

  INTEGER  A[*]

Po takiej deklaracji umieszczenie w programie linijki:

  I=A[5]

spowoduje, że I otrzyma wartość zmiennej A z piątego procesora. Naturalnie opuszczenie nawiasów tzn.

  I=A

oznacza, że każdy CPU przypisze I swoją własną wartość zmiennej A (inaczej można to też zapisać jako I=A[THIS_IMAGE()]).

Przy pomocy współtablic możemy zarówno odczytywać dane z innych procesorów, jak i wpisywać nowe wartości do nich.

Synchronizacja działań - klucz do sukcesu

Wydawałoby się, że możliwość ustalenia numeru procesora w połączeniu z mechanizmem wymiany informacji to już wszystkie składniki potrzebne do napisania sprawnego programu równoległego. Tak jednak nie jest...

Należy pamiętać, że na każdym CPU program wykonuje się zupełnie niezależnie, w szczególności nie ma gwarancji, że instrukcje przetwarzane są we wszystkich obrazach z idealnie taką samą szybkością. Tak nie jest nawet w przypadku fizycznie identycznych procesorów (w grę wchodzą tu różne czynniki od sprzętowych po programowe związane np. z funkcjonowaniem systemu operacyjnego).

Przykładowo pisząc:

I=A[5]

powinniśmy być pewni, że procesor 5 już obliczył wartość zmiennej A, o którą nam chodzi. To, że w kodzie naszego programu obliczenie to znajduje się wcześniej niż powyższe przypisanie wcale niczego nie gwarantuje! Procesor numer 5 mógł z jakichś przyczyn utknąć we wcześniejszych partiach kodu i jeszcze w ogóle do obliczania A nie dotrzeć!!!

Najprostszym sposobem zabezpieczenia się przed takimi sytuacjami jest wywołanie SYNC_ALL(). Procedura ta gwarantuje, że powrót z niej nastąpi dopiero, gdy wszystkie obrazy zakończą swoje operacje na współtablicach, występujące w kodzie źródłowym programu przed jej wywołaniem.

Dodatkowo jako argument możemy podać numer obrazu (lub nawet całą tablicę takich numerów), co ograniczy oczekiwanie tylko do wymienionych procesorów. I tak np.

CALL SYNC_ALL(5) 
I=A[5]

gwarantuje, że zmienna I we wszystkich obrazach otrzyma wartość obliczoną wcześniej przez procesor 5.

No dobrze, ale jak to wszystko wykorzystać? Prosty przykład

Oczywiście zebrane powyżej informacje nie wyczerpują całości standardu CAF, jednak w zupełności wystarczają do tworzenia własnych programów równoległych. Aby się o tym przekonać artykuł zakończymy prostym przykładem.

Nasz program będzie obliczał przybliżenie liczby pi korzystając ze wzoru:

\pi = 4 \int_0^1 \frac{1}{1+x^2}

i całkowania numerycznego metodą prostokątów. Wersja szeregowa programu może wyglądać na przykład tak (pi_serial.f90):

PROGRAM COMPUTE_PI_SERIAL
                                                                               
DOUBLE PRECISION :: PI, SUM, X, W
                                                                               
WRITE(6,*) 'Enter nuber  of intervals';
READ(5,*) N
WRITE(6,*) 'Number of intervals = ',N
                                                                               
W = 1.D0/N;
SUM = 0.D0

DO I= 1,N                                            ! Główna pętla. 
  X = W * (I - 0.5D0);                               ! Tę część pracy trzeba podzielić między procesory.
  SUM = SUM + 4.D0/(1.D0+X*X)                        !
ENDDO
                                                                               
PI =  W * SUM
WRITE(6,*) 'COMPUTED PI = ', PI
END

A jak przygotować wersję równoległą? Zasadnicza część pracy programu znajduje się w pętli obliczającej wartość zmiennej SUM. Najprostszym spospobem zrównoleglenia jest podzielenie sumowania na części i obliczenie każdej z nich na innym CPU. Potem pozostaje już tylko połączenie zebranych wyników. Oto przykładowa realizacja tego pomysłu w CAF (pi_parallel.f90):

PROGRAM COMPUTE_PI_PARALLEL
                                                                              
DOUBLE PRECISION :: MYPI[*], PI,PSUM,X,W
INTEGER :: N[*], ME,NIMG,I
                                                                               
        
NIMG = NUM_IMAGES()
ME = THIS_IMAGE()
                           

IF (ME==1) THEN                                      ! Tylko procesor 1 realizuje:
  WRITE(6,*) 'Enter number of intervals'             ! 
  READ(5,*) N                                        ! * wczytanie danych wejściowych,
  WRITE(6,*) 'Number of intervals= ',N               !
  DO I=2,NIMG                                        ! * rozesłanie ich to wszystkich obrazów.
    N[I] = N                                         !
  ENDDO                                              !
ENDIF

CALL SYNC_ALL(1)                                     ! Tu trzeba poczekać aż procesor 1 skończy
                                                     ! przygotowywać dane wejściowe.
  
W = 1.D0/N                                           ! Wszystkie procesory obliczają  
PSUM = 0.D0                                          ! swoje sumy częściowe.
DO I= ME,N,NIMG                                      ! 
  X = W * (I - 0.5D0);                               ! 
  PSUM = PSUM + 4.D0/(1.D0+X*X)                      !
ENDDO                                                !                                
MYPI = W * PSUM
                                                                               
CALL SYNC_ALL()                                      ! Znowu trzeba poczekać aż wszystkie CPU skończą 
                                                     ! obliczenia, wtedy będzie można zebrać wyniki. 
                                                                                                                                                               
IF (ME==1) THEN                                      ! Tylko procesor 1 realizuje: 
  PI = 0.D0                                          ! 
  DO I=1,NIMG                                        ! * połączenie sum częściowych, 
    PI = PI +  MYPI[I]                               !
  ENDDO                                              ! 
  WRITE(6,*)  'COMPUTED PI = ',PI                    ! * wypisanie wyniku.
ENDIF                                                !                                                                                
END

Jak i gdzie mogę uruchomić mój program w CAF? Technikalia

W ICM w CAF można programować na komputerze tornado (Cray X1E). Poniżej podamy kilka wskazówek, jak kompilować i uruchamiać programy właśnie na tej maszynie. Oczywiście za przykład posłuży nam programik pi_parallel.f90 z poprzedniego paragrafu.

Na początku na pewno będziemy chcieli zacząć od kompilacji. Ze względu na specyfikę Cray'a X1 kompilacja jest możliwa tylko w specjalnie do tego przeznaczonym katalogu /cpes/. Należy tam utworzyć swój własny podkatalog, gdzie zostanie nagrany program pi_parallel.f90. Kompilację uruchamiamy przez:

 ftn -Z -Ossp -opi_parallel pi_parallel.f90

Otrzymany plik binarny pi_parallel należy przenieść w jakieś sensowne miejsce np. do katalogu domowego lub katalogu /tmp1/. Trzymanie niewykorzystywanych plików na /cpes/ nie jest mile widziane, gdyż zajmują one niepotrzebne miejsce na tym systemie plików.

Następnie uruchamiamy (dla przykładu na trzech procesorach):

 aprun -n3 ./pi_parallel

w wyniku powinniśmy dostać:

 Enter number of intervals
 10
 Number of intervals=  10
 COMPUTED PI =  3.1424259850010983                                                                            

Jeśli tornado jest akurat obciążone (lub kiedy zażądamy dużej liczby procesorów) może okazać się, że nasze zadanie musi poczekać na wolne zasoby. Nie należy się więc niepokoić jeśli po wpisaniu komendy aprun przez jakiś czas nic się nie będzie działo. Oczywiście duże programy obliczeniowe napisane w CAF należy uruchamiać jedynie za pośrednictwem systemu kolejkowego.

Zamiast podsumowania

W powyższym artykule zostały opisane podstawy programowania równoległego w Co-Array Fortranie. Oczywiście na pewno nie udało nam się wyczerpać tego obszernego tematu. Czytelników zainteresowanych dodatkowymi informacjami zachęcamy do zapoznania się z następującymi odnośnikami: