Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

BOINC — zabawmy się graficznie...

W poprzednim wpisie opisałem pokrótce historię obliczeń rozproszonych oraz platformy BOINC oraz omówiłem trochę działanie serwera z punktu widzenia administratora projektu, natomiast dzisiaj chciałbym się bliżej przyjrzeć zagadnienie różnic w aplikacjach CPU oraz GPU oraz różnych architektur, na których oprogramowanie to może działać. Postaram się także przybliżyć proces portowania aplikacji CPU na platformę OpenCL na przykładzie rzeczywistego projektu naukowego.

CPU, czyli od tego się zaczęło…

Dla nikogo chyba nie jest zaskoczeniem, że podstawą wszelkich obliczeń na komputerach są programy wykonywane przez jego procesor, zupełnie jak każda aplikacja. W programie takim (w pewnym uproszczeniu) kolejne instrukcje wykonywane są kolejno po sobie w przypadku aplikacji jednowątkowej, a zrównoreglenie obliczeń następuje poprzez wysyłanie tej samej aplikacji do wielu komputerów i uruchomienie jej z różnymi parametrami startowymi. Oczywiście istnieją na platformie BOINC również projekty których aplikacje są programami wielowątkowymi, jednak stanowią mniejszość gdyż nic nie stoi na przeszkodzie, aby aplikację jednowątkową uruchamiać równocześnie na kilku wątkach maszyny wolontariusza.
Generalnie, aplikacje CPU znajdują zastosowanie do każdej kategorii obliczeń, gdyż mogą „obsłużyć” wszystkie instrukcje procesora głównego naszego komputera.
Ta uniwersalność współczesnych procesorów ma ogromną wpływ na przenoszenie aplikacji pomiędzy różnymi systemami operacyjnymi, a nawet architekturami systemowymi. W tej chwili, na platformie BOINC można spotkać niemal wszystkie platformy systemowe na rynku:

x86 (Windows, Linux, MacOS, NetBSD)
x86_64 (jw.)
ARM (Linux, Android)
Motorola (NetBSD, Linux)
MIPS (Linux)
PowerPC (Linux)

Do jednych z najlepiej obsługiwanych pod tym względem projektów należą te prowadzone przez naszych rodaków: Enigma@Home, Radioactive@Home oraz Universe@Home dla których aplikacje można znaleźć prawie do wszystkiego, co ma system operacyjny (tak, dobrym przykładem na to jest fakt, że swego czasu wśród wspieranych platform w projekcie Radioactive@Home był jeden z tunerów TV-Sat)…

Osobną kategorią aplikacji CPU można określić wysyłanie na komputery klientów gotowych maszyn wirtualnych skonfigurowanych optymalnie do wykonywania konkretnych obliczeń i zapewniających aplikacjom spójne środowisko niezależnie od używanej platformy. Niestety, taki sposób dystrybucji aplikacji niesie za sobą również wiele ograniczeń i generalnie nie jest powszechny (aczkolwiek jest szeroko stosowany w projektach związanych z CERN).

Istnieje również dość ciekawa grupa aplikacji CPU nazywanych NCI – Non CPU Intensive, czyli aplikacje nie obciążające CPU. Choć są one wykonywane na procesorze głównym komputera to jednak należą one do zadań, które prawie wcale nie wykonują skomplikowanych obliczeń i służą głównie do zbierania danych, np. z czujników podłączonych do komputera – tu przykładem jest aplikacja Radioactive@Home, która co ok. 34 sekundy pobiera dane z czujnika radioaktywności podłączonego do komputera i przesyła je na serwer projektu, oraz czujnik wstrząsów używanych w projekcie związanym z detekcją wstrząsów tektonicznych. Istnieje także aplikacja projektu WUProp@Home, która zbiera dane statystyczne innych aplikacji liczących na danym komputerze (nazwa aplikacji, projekt, czas wykonywania) i te dane wysyła na serwer projektu i udostępnia te dane (oczywiście zanonimizowane) wszystkim zainteresowanym na specjalnie stworzonej stronie internetowej.

GPU, czyli w kupie siła…

Jednostka centralna komputera osobistego jest niewątpliwie bardzo szybka i uniwersalna, jednak ma swoje ograniczenia. Uniwersalność niestety nie idzie czasami w parze z optymalizacją, dlatego inżynierowie i programiści zaczęli szukać nowych dróg na przyśpieszenie niektórych obliczeń. Niespodziewanie z pomocą im przyszły… karty graficzne!
Natomiast ciekawym jest, w jaki sposób karty graficzne mogą pomóc w obliczeniach? Otóż, od kilkunastu lat w procesorach kart graficznych producenci umieszczają jednostki arytmetyczne w celu przerzucania części obliczeń służących generowaniu obrazu z procesora głównego do procesora graficznego. Warto zauważyć, że choć prekursorem na tym polu była firma nVidia ze swoją technologią CUDA, to jednak szerzej jest stosowany (przynajmniej na platformie BOINC) jest otwarty standard OpenCL stworzony przez firmę Apple a rozwijany przez konsorcjum Kronos i zaadaptowany do swoich kart graficznych przez firmę AMD, głównie z powodu otwartości platformy, co wiąże się z silnym wsparciem innych producentów procesorów graficznych – obecnie OpenCL jest wspierany zarówno przez AMD, jak i nVidię, Intela oraz niektórych producentów architektury ARM (np. Hardkernel w swoim niezwykle ciekawym komputerze Odroid XU4 procesorem Exynos 5422). Dlatego też, w projekcie Universe@Home zdecydowaliśmy się oprzeć na platformie OpenCL.

Czym właściwie jest OpenCL?

Jak podaje Wikipedia:

„OpenCL (ang. Open Computing Language) – framework wspomagający pisanie aplikacji działających na heterogenicznych platformach składających się z różnego rodzaju jednostek obliczeniowych (m.in. CPU, GPU). Główną zaletą OpenCL-a jest to, że można użyć jednego otwartego standardu zamiast zamkniętych wspierających sprzęt tylko jednego producenta (np. CUDA tylko dla kart graficznych produkowanych przez Nvidia).”

W przypadku kart graficznych mamy do dyspozycji sporą liczbę dość jednak ograniczonych jednostek obliczeniowych, które mogą operować (znowu, w pewnym uproszczeniu) na macierzach i wektorach do nich. Powoduje to, że w stosunku do CPU możemy wykonać bardzo wiele prostych zadań za pomocą karty graficznej równocześnie na wielu kernelach, jednak nie mamy możliwości prostego skompilowania aplikacji przewidzianej do współpracy z CPU bez znacznych nakładów pracy polegającej na przystosowanie kodu źródłowego do pracy równoległej na GPU.

Aby przybliżyć problemy stojące przed osobami chcącymi przenieść aplikację CPU posłużę się naszą bieżącą pracą, czyli przystosowywaniem aplikacji projektu do wykonywania kodu OpenCL, głównie za pomocą kart graficznych ze stajni AMD.

Aplikacja projektu Universe@Home – czyli jak ugryźć cegłę…

Na początek warto sobie zdać sprawę, że aplikacja używana w różnych wersjach w naszym projekcie to program, którego rozwój zaczął się w 2002 (StarTrack, autorstwa prof. Krzysztofa Bełczyńskiego), a sam kod źródłowy był tworzony bez uwzględnienia ewentualnego zrównoleglania zadań na akceleratorach graficznych i liczy sobie ponad 20tyś linii kodu źródłowego. Aplikacja ta służy do symulacji ewolucji układów gwiezdnych, poprzez wykonywanie poszczególnych kroków symulacji sekwencyjnie. W związku z tym, w aplikacji występuje spora liczba skoków warunkowych, których ilość jest za każdym razem inna a same skoki odwołują się do wielu różnych funkcji w programie. Dodatkowo, wszystkie prawie pętle wykonywane warunku jako swoje argumenty początkowe traktują dane uzyskane z poprzednich funkcji lub wykonań, w związku z tym prawie niemożliwe jest prawidłowe przewidzenie liczby wykonań danej pętli w celu zoptymalizowania portowania obliczeń na GPU…
Dodać należy także, że aplikacja prawie wszystkie operacje wykonuje na liczbach podwójnej precyzji zatem asortyment możliwych do wykorzystania kart graficznych zawiera tylko te, które to umożliwiają.
Skoro znamy już ograniczenia oraz potencjalne źródła problemów, warto przejść do konkretnych wyliczeń. Podczas profilowania aplikacji, okazało się, że przez około 95% czasu pracy, aplikacja wykonuje funkcje matematyczne takie jak logarytmowanie i potęgowanie. Dodatkowo, kod źródłowy był kompilowany z uwzględnieniem agresywnej optymalizacji kompilatora, dzięki czemu dość spora część potęgowania była upraszczana do mnożenia. Jednak pomimo zastosowania tychże optymalizacji nadal około 90% czasu pracy, to były wspomniane funkcje potęgowania i logarytmowania. Szczęśliwie, te funkcje pomimo swojego skomplikowania szybko działają na GPU (Radeon), pomimo że najczęściej takie obliczenia procesory karty te wykonują w 1/16 szybkości obliczeń dla pojedynczej precyzji (wynika to z architektury kart AMD w których jednostki zmiennoprzecinkowe stanowią 1/16 stałoprzecinkowych).
Warto w tym miejscu zauważyć, że pojedyncze wykonanie programu powoduje wykonanie określonej liczby symulacji ewolucji układu gwiazdowego, przy czym zwiększenie liczby symulacji powoduje zwiększenie precyzji całości symulacji. Dla CPU standardowo stosuje się 20’000 symulacji, które na średniej klasy komputerze wykonują się około 2 godzin. Oprócz samego przyśpieszenia działania obliczeń, celem przeniesienia aplikacji do akceleratora graficznego jest również zwiększenie precyzji obliczeń poprzez zwiększenie ilości kroków symulacji podczas pojedynczego uruchomienia programu.

#### Tabela wykonań funkcji w głównym kodzie (liczba symulacji=162065)

Nazwa | Ilość wykonań | Czas w nanosekundach
-------------------------------------------------------
main | 1 | 756532234150
singl1 | 5574822 | 166850210333
singl2 (dMmtf) | 7081388 | 510538375493
orb_change | 1507166 | 44933615186
dMmtf | 83850 | 525637959062
dMgainf | 25172850 | 4358598626
tmerge1 | 10499 | 357275868
tmerge2 (Xbin) | 56415 | 13751031312
Xbin | 56415 | 13794075608
decide | 24895 | 12698941931

Idealną sytuacją by było przeniesienie całości obliczeń do OpenCL, jednak z powodu wielokrotnych rozgałęzień podczas działania oraz minimalnej ilości wykonań części funkcji nacisk położyliśmy głównie na przeniesienie najbardziej czasochłonnych obliczeń na akceleratory graficzne, przy pozostawieniu niektórych funkcji dla CPU. Nadal jednak pozostaje niezwykle istotne pytanie – w jaki sposób przewidzieć, jakie funkcje i jak wiele razy będą wykonywane przy zdecydowanie różnych parametrów startowych aplikacji liczącej? W końcu w tej chwili mamy około 60 różnych parametrów w 3 różnych rodzajach symulacji (a co kilka miesięcy pojawiają się nowe).
Po długim zastanowieniu wydaje się, że zadanie to jest w praktyce niemożliwe, w końcu zmiana jednego parametru skutkuje zupełnie innym rozkładem wywołań funkcji… Dlatego musimy stworzyć narzędzie (nazwane CLRunEngine), które będzie służyło do przewidzenia tych wartości, w dużym skrócie, zmiana kodu źródłowego musi skutkować dodaniem przez programistę odpowiednich dyrektyw w kodzie źródłowym, które silnik będzie mógł zinterpretować w celu ustalenia kolejności funkcji oraz ich liczby, wartości wstępnych itd. Choć zadanie wydaje się skomplikowane, to jednak ma docelowo ułatwić dokonywanie zmian w programie bez praco i czasochłonnego analizowania całości kodu w celu ustalenia potencjalnych wywołań funkcji.

Jako, że całość nadal jest w trakcie testów i dopracowywania nie mogę jeszcze przedstawić w pełni wiarygodnych testów wydajnościowych, jednakże już w tej chwili na podstawie testów jednostkowych można przewidywać, że dla karty AMD Radeon HD 7850 przyrost szybkości wykonywanych obliczeń dla poszczególnych funkcji będzie wynosił od 20 do 80 razy w stosunku do procesora Core i5 3470 3.9GHz...

Platform: AMD Accelerated Parallel Processing
Device: Pitcairn
Executing kernel: Lbagbf
Samples: 816804
Time: 0.00685333
Time (CPU): 0.086123 seconds
Executing kernel: Leagbf2
Samples: 715404
Time: 0.0134536
Time (CPU): 0.220607 seconds
Executing kernel: Lgbf2
Samples: 1000000
Time: 0.0167493
Time (CPU): 0.316528 seconds
Executing kernel: Lhe1f
Samples: 1000000
Time: 0.00458119
Time (CPU): 0.051992 seconds
Executing kernel: Lhgf
Samples: 1000000
Time: 0.041496
Time (CPU): 1.43043 seconds
Executing kernel: Lhsgbf
Samples: 1000000
Time: 0.003652
Time (CPU): 0.225244 seconds
Executing kernel: Lhsmsf
Samples: 1000000
Time: 0.00755867
Time (CPU): 0.398542 seconds
Executing kernel: Lmsf
Samples: 1000000
Time: 0.0436181
Time (CPU): 4.55439 seconds
Executing kernel: Ltagbf2
Samples: 1000000
Time: 0.0166179
Time (CPU): 0.325152 seconds

Gdzie „Time” to czas wykonania operacji na GPU, „Time (CPU)” te same operacje wykonywane na CPU.

W następnym wpisie postaram się już zamieścić testy z wykorzystaniem aplikacji w wersji beta wraz z porównaniem dla kilku modeli kart graficznych w połączeniu z różnymi kartami ze stajni AMD. Nie ukrywam także, że wyzwaniem będzie także przystosowanie programu do działania na innych platformach, w tym ARM co pozwoli na solidne przetestowanie wydajności układów Mali w komputerze ODROID XU4 w realnych obliczeniach przydatnych w zastosowaniach naukowych. Zapraszam również do wspólnego liczenia różnych projektów naukowych w ramach naszej narodowej drużyny BOINC@Poland :) 

linux programowanie serwery

Komentarze

0 nowych
  #1 26.10.2016 13:06

Dobry artykuł.
Zgadzam się z faktem, że Nvidia z CUDA była pierwsza to AMD z OpenCL wyprzedziło ją i to bardzo widocznie. Taka sytuacja być może jest podyktowana tym, że CUDA jest nastawiona według na gry i efekty do nich, a OpenCL już od lat wspiera wykorzystanie GPU w masowych obliczeniach

krzyszp   8 #2 26.10.2016 23:24

@goofyx (niezalogowany): To nie do końca prawda, na części superkomputerów wykorzystuje się CUDA, jednak w tym konkretnym przypadku OpenCL jest po prostu bardziej uniwersalne.