Views

Biuletyn nr 19

From KDMwiki

Jump to: navigation, search
Biuletyn KDM
1 | 2 | 3 | 4 | 5
6 | 7 | 8 | 9 | 10
11 | 12 | 13 | 14
15 | 16 | 17 | 18
19 | 20 | 21 | 22
23 | 24 | 25 | 26
27 | 28 | 29 | 30
31 | 32
Lista biuletynów

Biuletyn nr 19 (12 lutego 2007).

Spis treści

Nowa konfiguracja systemu kolejkowego na halo

Autor: Łukasz Bolikowski

W ostatnim czasie zespół KDM we współpracy z administratorami przeprowadził optymalizację ustawień schedulera Maui odpowiadającego za szeregowanie zadań na klastrze halo. Wykorzystując informacje o zadaniach wstawianych do systemu kolejkowego w ciągu ostatnich 6 miesięcy, staraliśmy się tak dobrać ustawienia, aby zmaksymalizować wykorzystanie klastra i zminimalizować czas oczekiwania w kolejkach.

Nowa konfiguracja aktywna jest od weekendu 3-4 lutego. Poniżej przedstawiamy najważniejsze różnice w korzystaniu z klastra halo.

Kolejka test

Tak jak dotychczas, użytkownicy mogą uruchamiać zadania w jednej z dwóch kolejek: halo lub test. Kolejka test służy do uruchamiania krótkich, co najwyżej 30-minutowych zadań testowych na niewielkiej liczbie procesorów. Kolejka ta ma zarezerwowane dwa dwuprocesorowe węzły na wyłączność, może jednak korzystać, bez rezerwacji, z całego klastra (w tym z nowszych, 8-procesorowych węzłów v40z).

Kolejka halo

Domyślną kolejką pozostaje halo. W poprzedniej konfiguracji zadania były ostatecznie przenoszone przez system do jednej z kilkunastu kolejek o nazwach: halo_1n_24h, halo_4n_336h, itd. W obecnej sytuacji halo jest jedyną kolejką obliczeniową. W okresie przejściowym widoczne będą jeszcze niektóre "stare" kolejki - do czasu aż zakończą się ostatnie zadania w nich uruchomione.

Limity czasu

W obecnej sytuacji jeszcze bardziej niż dotychczas opłaca się ściśle szacować czas działania programu - nie ma już sztucznie utworzonych progów 24h, 168h, 336h.

Aby unaocznić znaczenie dobrej deklaracji czasu wykonania, przedstawmy polecenie showbf. Pokazuje ono dostępne w danej chwili "okna" wykonania krótkich bądź małych zadań. W największym skrócie: system rezerwuje (nieraz w dość odległej przyszłości) czas procesorów dla dużych zadań, próbując zatkać powstałe "dziury" przy pomocy krótszych i mniejszych zadań. Dopełnianie małymi zadaniami nazywane jest backfill, stąd nazwa polecenia: showbf. Oto przykład użycia (oprócz partycji v40z w systemie są też: e325 i test):

# showbf -p v40z -S
            HostName Procs Memory    Disk    Swap   Time Available
          ---------- ----- ------ ------- -------   --------------

                n102     1  15082    1801   25000       1:09:05:18

                n104     1  14050   22281   25000       1:09:05:18

                n106     1  13590   22281   25000       1:06:38:45

                n107     1  14562   22281   25000       1:09:05:18

                n111     5  15126   22281   25000       1:09:05:18

                n112     7  16212   83721   25000       1:09:05:18

Dowiadujemy się, że gdybyśmy zażądali dwóch węzłów po pięć procesorów na 1 dzień i 12 godzin (36 godzin), to ustawieni zostalibyśmy w kolejce z rezerwacją daleko w przyszłości, ponieważ "od zaraz" system nie dysponuje tyloma procesorami dostępnymi na taki okres czasu. Jeśli natomiast zmiejszymy deklarację czasu wykonania do 1 dnia i 8 godzin, zadanie ma szansę być uruchomione od razu, ponieważ mieści się w dostępnym oknie.

Oczywiście, tak jak dotychczas, zadania które przekroczą zadeklarowany czas wykonania są natychmiast kończone, co w większości wypadków oznacza utratę wszystkich wyników obliczeń. Podsumowując: należy z jednej strony starać się ściśle szacować czas wykonania programu, z drugiej zaś strony dołożyć starań, aby nie zadeklarować zbyt niskiej wartości.

Liczba procesorów na węzeł (ppn)

Dotychczas istniał skrypt systemowy poprawiający ustawienia wartości ppn, na przykład deklaracja:

#PBS -l nodes=2:ppn=2

przekształcana była na:

#PBS -l nodes=1:ppn=4

Było to spowodowane potrzebą dociążenia nowszej części klastra w początkowym okresie funkcjonowania.

W obecnej sytuacji skrypty systemowe nie zmieniają deklarowanych przez użytkowników wartości. W związku z tym zadania deklarujące ppn ≥ 2 na pewno trafią na węzły v40z, pozostałe zaś najprawdopodobniej na węzły e325.

Wyniki symulacji

Oto statystyki symulacji czasu oczekiwania w kolejce dla różnych wariantów konfiguracji:

Konfiguracja Średni Odch. std. Maksymalny
Oryginalna konfiguracja 1:06:35:27.913 5:03:21:33.890 59:22:06:50.000
Nowa konfiguracja 1:06:18:17.260 2:21:33:47.767 33:22:12:00.000

Czasy oczekiwania w kolejce przy nowej konfiguracji, z podziałem na klasy zadań:

Typ zadania Średni Odch. std. Maksymalny
Testowe 0:00:01:01.166 0:00:05:50.916 0:03:20:00.000
SMP (ppn > 2) 2:02:04:22.212 3:13:47:59.488 22:13:56:00.000
Lekkie (nodes*ppn ≤ 2) 1:09:24:40.144 3:00:55:29.686 33:22:12:00.000
Ciężkie (ppn ≤ 2 i nodes*ppn > 2) 0:23:00:45.473 2:01:53:45.974 14:06:24:00.000

Podsumowanie

Przeprowadzona optymalizacja konfiguracji skróciła znacząco "ogon" w rozkładzie czasów oczekiwania w kolejce, skrócił się też nieznacznie średni czas oczekiwania. Dalsze skrócenie średniego czasu oczekiwania osiągnąć można przez ściślejsze deklarowanie czasu wykonania i zużywanej pamięci, co pozwoli schedulerowi efektywniej planować szeregowanie zadań. W ostatnim półroczu stosunek rzeczywistego czasu wykonania do zadeklarowanego wyniósł średnio ok. 25%. Kolejny krok należy więc do użytkownika!

Przypomnijmy, że obecnie, podobnie jak to miało miejsce dotychczas, system kolejkowy bierze pod uwagę przede wszystkim następujące trzy deklaracje:

#PBS -l walltime=HHH:MM:SS
#PBS -l nodes=X:ppn=Y
#PBS -l mem=ZZZmb

Narzędzia: Jak labelować obrazki?

Autor: Maciej Szpindler

Załóżmy, że chcemy opublikować obrazki lub film jako wizualizacje wyników naszej pracy. Wiemy jak stworzyć odpowiednie pliki graficzne (filmy, patrz artykuł w ostatnim biuletynie: Jak wygenerować film z serii obrazów), ale chcemy je jeszcze dodatkowo podpisać, tak aby podpis znajdował się na obrazie. Można zrobić to automatycznie za pomocą narzędzi ImageMagick dostępnych w większości "Linuxów".

Zadanie

Mamy obrazek image.png lub serię obrazów image*.png. Chcemy aby na każdym z naszych obrazów został wstawiony ten sam podpis: Moje obrazy


Rozwiązanie: jeden obraz

Użyjemy narzędzia convert z pakietu ImageMagick. Posiada ono wiele możliwości przekształcania obrazu; zacznijmy od umieszczenia napisu na naszym obrazie:

convert -draw "text 25,60 'Mój obraz'" image.png image_ann.png

Umieści napis Mój obraz zaczynając od punktu (x = 25, y = 60 liczone od góry) na naszym obrazie image.png zapisując wynik w pliku Tmage_ann.png.

Napis wydaje się nieco mały i czcionka nie taka jak trzeba, dodajmy:

convert -draw "text 25,60 'Mój obraz'" -font Helvetica -pointsize 72 image.png image_ann.png

Uwzględni rozmiar i rodzaj czcionki jaka ma zostać użyty do zrobienia podpisu.

Podobnie mamy do dyspozycji wiele innych opcji, przykładowo wypełnienie -fill color.

Rozwiązanie: seria obrazów

Dla serii obrazów możemy posłużyć się metodą opisaną w poprzednim artykule, dla podpisania obrazów, w pętli stosując do każdego obrazu powyższą procedurę (w powłoce bash):

for i in `seq 1 99` ; do ii=`printf "%03d" $i` ; convert -draw "text 25,60 'Mój obraz'" -font Helvetica -pointsize 72 image$i.png image_ann$ii.png  ; done

Następnie przekształcamy serię podpisanych obrazków w film:

convert image_ann*.png film.mpeg

Zobacz też

Narzędzia: Profiling Cray PAT na tornado

Autor: Maciek Cytowski

Komputer Tornado dostarcza programiście kilku ułatwiających mu życie narzędzi. Wśród nich wyróżnić należy przede wszystkim kompilatory wraz z ich listingami oraz narzędzia C ray Performance Analysis Tools (w skrócie Cray PAT). Te pierwsze były już opisane przez Łukasza Bolikowskiego w 17 numerze Biuletynu (Biuletyn nr 17). W tym artykule postara m się przybliżyć Państwu narzędzia Cray PAT. Sposób ich użycia zaprezentuję na pouczającym przykładzie optymalizacji kodu na architekturę Cray X1E.

Dział kdm-support otrzymał ostatnio zadanie optymalizacji pewnego kodu. Ponieważ program ten jest napisaną w Fortranie aplikacją o pokaźnej ilości linii, wkrótce okazało się, że zadanie to wymaga użycia specyficznych narzędzi w celu zidentyfikowania i optymalizacji najbardziej czasochłonnych fragmentów kodu. Zidentyfikowany "ciężki" fragment można streścić do następujących linii:

     1         Program Test
     2         Implicit None
     3         Integer i,j,k
     4         Real Table(1000,1000,1000)
     5         Real maximum
     6         Do k = 1,1000
     7           Do j = 1,1000
     8             Do i = 1,1000
     9               Table(i,j,k)=i*i-j*j+k
    10             End Do
    11           End Do
    12         End Do
    13         maximum=0.0
    14         Do k = 1,1000
    15           Do j = 1,1000
    16             Do i = 1,1000
    17               if( Table(i,j,k) .gt. maximum) maximum=Table(i,j,k)
    18             End Do
    19           End Do
    20         End Do
    21         PRINT *,'Maximum=',maximum
    22         End

W linii 4 definiowana jest duża tablica liczb rzeczywistych (Table), którą następnie w liniach 6-12 wypełniamy pewnymi liczbami. Reszta kodu ma za zadanie odnaleźć maksymalną wartość w tablicy Table. Znajdowanie największej wartości zaimplementowane jest w trzech pętlach w liniach 14-20.

Oczywiście w oryginalnym kodzie tablica nie była tworzona wewnątrz funkcji lecz przekazywana do niej jako parametr. Taka postać przykładu jest jednak dużo prostsza i czytelniejsza.

Oryginalny kod posiadał plik automatycznej kompilacji Makefile, w którym zdefiniowane były reguły kompilacji naszej funkcji.

Wpierw skompilujmy kod na SSP (z opcjami przyjętymi w pliku Makefile):

ftn -hssp -Oaggress,scalar2,vector2,stream0,nopattern,task0,fp0 -rm -c Test.F

i utwórzmy plik binarny Test.exe:

ftn -hssp -Oaggress,scalar2,vector2,stream0,nopattern,task0,fp0 -rm -o Test.exe Test.o

Oprócz specyficznych opcji optymalizacji (wszystkie wymienione po -O) widzimy tutaj również opcję -rmo odpowiadającą za generowanie listingów kompilatora.

Aby wykonać poniższe ćwiczenia powinno nam wystarczyć 15 minut i jeden procesor SSP - czyli użyjemy do tego kolejki test. Wywołujemy:

qsub -I -q test -N test_pat -l mppssp=1

Sprzętowe liczniki programu - pat_hwpc

Pierwszym dobrym pomysłem jest zawsze sprawdzenie jaka jest wydajność naszego kodu. Do sprawdzenia liczników sprzętowych związanych z naszym programem służy na Cray'u narzędzie pat_hwpc.

Wywołajmy:

pat_hwpc aprun Test.exe

Na ekranie wyświetlone zostaną informacje o wykorzystanych przez program zasobach, m.in.:

 Vector FP ops              227 /sec        20702 ops        0.0%
 Scalar FP ops           10.990M/sec   1000000187 ops       100.0%
 Total  FP ops           10.991M/sec   1000020889 ops

oraz:

 PE      PID  User time  System time  Module  Memory resident
              (seconds)    (seconds)            size (MBytes)

  0  3969015  91.215197     3.051721       6      3888.187500

Oznacza to, że nasz kod wykonywał się około 91 sekund oraz był w większości programem skalarnym. Bardzo ważnym dla nas licznikiem w dalszej części optymalizacji będzie liczba operacji zmiennoprzecinkowych na sekundę, tutaj 10.991M/sec.

Raporty Cray PAT

Chcemy teraz zidentyfikować obliczeniowe jądro programu. Do tego celu skorzystamy również z narzędzi Cray PAT. Wpierw należy przygotować binarkę instrumentalną:

pat_build Test.exe Test.exe.inst

a następnie ją uruchomić:

aprun Test.exe.inst

Zostanie wyświetlony komunikat podobny do następującego:

CrayPat:  Version 25.59  04/19/06 18:02:21
CrayPat:  Runtime summarization enabled. Set PAT_RT_SUMMARY=0 to disable.
Maximum= 1000999.
Experiment data file(s) written: 
/cpes/sheed/examples/Test.exe.inst+3971311pt.xf 
97.594u 4.590s 1:43.66 98.5%    0+0k 158+0io 52pf+0w

Jak widać został utworzony plik Test.exe.inst+3971311pt.xf, który zawiera informacje dotyczące wykonania programu. Informacje te można wczytać za pomocą programu pat_report. Możemy zarządać aby w wyniku swojego działania program ten wygenerował różnego rodzaju raporty. Ja osobiście zwykle używam tego programu z następującymi opcjami:

pat_report -b source,function,line Test.exe.inst+3971311pt.xf

Wywołanie powyższego spowoduje wygenerowanie raportu z dokładnymi informacjami, w których miejscach nasz kod spędza najwięcej czasu z wyróżnieniem nazwy funkcji, numeru linii oraz dokładnego źródła, z którego pochodzą. W tym wypadku najważniejsza część tego raportu wygląda następująco:

100.0% |    100.0% | 10056 |Total
|---------------------------------------
| 100.0% |    100.0% | 10055 |/cpes/sheed/examples/Test.F
|        |           |       | main
|||-------------------------------------
|||  95.7% |     95.7% |  9626 |line.17
|||   3.0% |     98.7% |   303 |line.1
|||   1.3% |    100.0% |   126 |line.8
|||=====================================

Oznacza to, że program spędził najwięcej czasu w linii 17 naszego kodu. Czyli trafiliśmy, jak można się było tego spodziewać, w pętlę znajdującą maksymalną wartość w tablicy Table.

Dlaczego tak się dzieje?

Dzięki temu, że kompilacja została dokonana z opcją -rm możemy sprawdzić jakich optymalizacji dokonał (lub nie dokonał) kompilator. Napiszmy:

less Test.lst

W listingu widzimy, że kompilator nie poradził sobie z wektoryzacją pętli z okolic linii 17.

  14.                  maximum=0.0
  15.  C-------<       Do k = 1,1000
  16.  C C-----<         Do j = 1,1000
  17.  C C r---<           Do i = 1,1000
  18.  C C r                 if( Table(i,j,k) .gt. maximum) maximum=Table(i,j,k)
  19.  C C r--->           End Do
  20.  C C----->         End Do
  21.  C------->       End Do


Rozwiązanie - optymalizacja

Rozwiązania istnieją co najmniej dwa. Po pierwsze bardzo rzadko spotykane jest użycie opcji kompilacji -Ofp0. Opcja ta służy do "ekstremalnego dopasowania" naszego kodu do reguł IEEE i wyłącza wiele innych metod automatycznej optymalizacji. W podręczniku "Cray Fortran Compiler Commands and Directives Reference Manual" możemy przeczytać następujący komentarz dotyczący tej opcji:

The -O fp0 option should never be used, except when your code pushes the limits of IEEE accuracy or requires strong 
IEEE standard conformance.

Usunięcie tej opcji lub podmiana na domyślne -Ofp2 od razu rozwiązuje problem. Oto odpowiedni fragment listingu:

  13.                  maximum=0.0
  14.  C-------<         Do k = 1,1000
  15.  C C-----<             Do j = 1,1000
  16.  C C V---<               Do i = 1,1000
  17.  C C V                   if( Table(i,j,k) .gt. maximum) maximum=Table(i,j,k)
  18.  C C V--->               End Do
  19.  C C----->             End Do
  20.  C------->         End Do

oraz sprzętowe liczniki programu:

 Vector FP ops            345.782M/sec  2000086057 ops       100.0%
 Scalar FP ops                 31 /sec         181 ops        0.0%
 Total  FP ops            345.782M/sec  2000086238 ops

i

 PE      PID  User time  System time  Module  Memory resident
              (seconds)    (seconds)            size (MBytes)

  0  4293154   5.839067     3.400911       2      3888.187500

Nie dość, że udało nam się zwiększyć liczbę wykonanych operacji zmiennoprzecinkowych na sekundę, to jeszcze nasz kod przyśpieszył jakieś 15 razy.

Jeżeli z jakichś bardzo poważnych powodów jesteśmy zmuszeni pozostawić opcję kompilacji -fp0, to i tak istnieje sposób uzyskania podobnej wydajności. Komputer Cray X1 posiada dość długą listę funkcji wbudowanych. Jedną z nich jest funkcja MAXVAL która służy do znajdywania maksymalnej wartości w zadanej tablicy. Więcej informacji można uzyskać pod man maxval. Przy jej użyciu nasz kod niezwykle się upraszcza:

     1         Program Test
     2         Implicit None
     3         Integer i,j,k,l
     4         Real Table(1000,1000,1000)
     5         Real maximum
     6         Do k = 1,1000
     7           Do j = 1,1000
     8             Do i = 1,1000
     9               Table(i,j,k)=i*i-j*j+k
    10             End Do
    11           End Do
    12         End Do
    13         maximum=0.0
    14         maximum=MAXVAL(Table)
    15         PRINT *,'Maximum=',maximum
    16        End

Na listingu widnieje duża litera V:

  13.                  maximum=0.0
  14.  V------<>       maximum=MAXVAL(Table)

a liczniki sprzętowe nie odbiegają od tych wygenerowanych wcześniej:

 Vector FP ops            365.376M/sec  2000086057 ops       100.0%
 Scalar FP ops                 33 /sec         181 ops        0.0%
 Total  FP ops            365.376M/sec  2000086238 ops

 PE      PID  User time  System time  Module  Memory resident
              (seconds)    (seconds)            size (MBytes)

  0  4293989   5.528357     3.214567       2      3888.187500

Poza tym kod nasz jest dużo bardziej elegancki, czy też dużo bardziej Cray-owy.

Podsumowanie

Narzędzia PAT na Cray-u stanowią niezbędną pomoc dla programisty i warto je znać. W połączeniu z listingami kompilatorów stanowią dosyć silną maszynę do optymalizacji. W pracy na Cray-u przydaje się również często dokumentacja, której bardzo obszerna część znajduje się na stronie:

http://docs.cray.com