Upload
duongtuyen
View
227
Download
1
Embed Size (px)
Citation preview
POLITECHNIKA ŁÓDZKA
WYDZIAŁ ELEKTROTECHNIKI,
ELEKTRONIKI,
INFORMATYKI I AUTOMATYKI
INSTYTUT INFORMATYKI STOSOWANEJ
Autoreferat
Algorytmy obliczeń równoległych z użyciem
procesorów graficznych (GPU) do przetwarzania
i wizualizacji danych pomiarowych
z przemysłowych systemów tomograficznych
mgr inż. Bartosz Matusiak
Promotor:
Prof. dr hab. inż. Dominik Sankowski
Promotor pomocniczy:
dr inż. Andrzej Romanowski
Łódź, 2013
Wprowadzenie
1
1. Wprowadzenie
W przeciągu ostatnich kilku lat można było zaobserwować spowolnienie w rozwoju
układów CPU (ang. – Central Processing Unit) przy jednoczesnym bardzo dynamicznym
rozwoju oraz rosnącym znaczeniu wykorzystania procesorów graficznych (ang. GPU –
graphical processing unit) do wykonywania obliczeń ogólnego przeznaczenia (ang. GPGPU
– General-Purpose computation on Graphics Processing Units) [Harris, 2003; Ikeda i inni,
2006]. Pomimo częstotliwości taktowania kilkukrotnie niższej niż układów CPU, układy GPU
umożliwiają uzyskanie nawet kilkunastokrotnego skrócenia czasu wykonywania obliczeń dla
algorytmów, które mogą być wykonywane w sposób równoległy. Obecnie zdecydowana
większość dostępnych na rynku komputerów klasy PC zawiera karty graficzne z układami
GPU umożliwiającymi przeprowadzenie na nich takich obliczeń [Lindholm i inni, 2008]. Tak
duża popularność układów GPU daje efekt w postaci bardzo korzystnego stosunku ceny do
mocy obliczeniowej jaką te układy dostarczają, a do tego nie zajmują one tyle miejsca ile
systemy wielomaszynowe o zbliżonej mocy obliczeniowej. Jednak, aby w pełni wykorzystać
moc obliczeniową dostarczaną przez układy GPU konieczne są modyfikacje istniejących lub
implementacje zupełnie nowych algorytmów, co jest spowodowane charakterem i specyfiką
obliczeń równoległych. Poza tym, oryginalne przeznaczenie układów GPU do przetwarzania
grafiki (przeważnie 3D) narzuca dodatkowe ograniczenia oraz niezbędne modyfikacje
algorytmów jak np. konieczność wymiany informacji pomiędzy pamięcią operacyjną układu
CPU oraz GPU, czy też minimalizację dostępów do pamięci operacyjnej układu GPU.
Skutkiem tego nawet algorytmy przeznaczone dla systemów wieloprocesorowych nie zawsze
sprawdzają się w przypadku zaimplementowania ich na układach GPU i wymagają pewnych
modyfikacji. Innym aspektem, który należy brać pod uwagę przy projektowaniu algorytmów
dla układów GPU jest fakt, że układy te są jednocześnie odpowiedzialne za operacje
graficzne, co z kolei daje nowe możliwości w zakresie wizualizacji przetwarzanych danych.
Mając na uwadze powyższe cechy układów GPU, należy zwrócić uwagę na
potencjalne korzyści wynikające z możliwości ich wykorzystania do przetwarzania danych
z przemysłowych systemów tomograficznych. Do tej pory opracowano bardzo wiele technik
tomografii procesowej. Bazują one przeważnie na właściwościach fizyko-chemicznych
substancji biorących udział w procesie. Stosowny dobór odpowiedniej techniki
tomograficznej zależy więc głównie od typu procesu jaki ma być monitorowany, ale także od
takich parametrów jak inwazyjność metody, czy też rozdzielczość czasowa lub przestrzenna.
Środowisko CUDA
2
Jedną z często wykorzystywanych technik tomograficznych jest elektryczna tomografia
pojemnościowa (ang. Electrical Capacitance Tomography – ECT) [Reinecke i Mewes, 2006].
Nośnikiem informacji są w tym przypadku pojemności pomiędzy elektrodami czujnika, które
wykorzystuje się do zobrazowania gęstości (rozkładu) przenikalności elektrycznej ε [F/m].
Ważną cechą pomiaru w ECT jest brak konieczności fizycznego kontaktu z badanym
medium. Niestety zrekonstruowanie obrazu prezentującego ten rozkład wymaga często
dużego nakładu obliczeń. Obliczenia te mogą zostać przyśpieszone dzięki zastosowaniu
układów graficznych.
2. Środowisko CUDA
Sprawne programowanie układów graficznych wymaga odpowiednich narzędzi
i środowiska programistycznego. Do najpopularniejszych rozwiązań w tym zakresie należą
obecnie [Verdiere, 2011]
CUDA (ang. Compute Unified Device Architecture ) – Jest to pierwsza dedykowana
dla GPU architektura zarówno sprzętowa jak i programowa. Została ona opracowana
przez firmę NVIDIA i zaprezentowana po raz pierwszy w listopadzie 2006 roku [NVIDIA
CUDA C Programming Guide, 2011]. Umożliwia programowanie z wykorzystaniem
takich języków jak C/C++, Fortran, czy Python. Zawiera także dodatkowe biblioteki, takie
jak np. CUBLAS dla podstawowych operacji algebry liniowej oraz CUFFT dla szybkiej
transformaty Fouriera [Kirk, 2007].
OpenCL (ang. Open Computing Language) – Jest otwartym i niezależnym od
platformy standardem zawierającym jednolity interfejs programowania aplikacji (API) do
programowania równoległego zarówno CPU jak i GPU z wykorzystaniem języka C.
Standard ten był początkowo rozwijany przez firmę Apple, która w 2008 roku zgłosiła
jego pomysł do Khronos Group w celu przyjęcia go jako otwartego standardu. Do
rozwijania OpenCL przyłączyły się także inne firmy z NVIDIA i ATI na czele. Wersja 1.0
standardu została opublikowana pod koniec 2008 roku, a jako pierwsza udostępniła go
firma Apple wraz z systemem Mac OS X Snow Leopard [Stone i inni, 2010].
Przedstawione w dalszej części tego artykułu rozwiązania wykorzystują architekturę
CUDA, która pomimo faktu, że można ją wykorzystywać wyłącznie z produktami firmy
NVIDIA jest obecnie najpopularniejszym rozwiązaniem dla obliczeń równoległych na
procesorach graficznych.
Środowisko CUDA
3
Uruchamianie programów w architekturze CUDA bazuje na tzw. kernelach (ang.
kernel). Kernel jest funkcją wykonywaną równolegle w wielu równoległych wątkach. Wątki,
które wykonują ten sam kernel i współdzielą pamięć, są razem synchronizowane i grupowane
w trójwymiarowe bloki wątków (ang. thread blocks). Z kolei bloki wątków są wywoływane
w trójwymiarowych gridach (ang. grid). Każdy z wątków ma swoją lokalną prywatną
przestrzeń pamięci. Analogiczne każdy blok wątków ma pamięć współdzieloną (ang. shared
memory) dostępną tylko dla wątków zawartych w danym bloku. Natomiast gridy mają dostęp
do pamięci globalnej przydzielonej do aplikacji [NVIDIA CUDA C Programming Guide,
2011]. Podział ten jak i hierarchię pamięci obrazuje schemat na rysunku 1. Aby wątki mogły
wykonać odpowiednie operacje są one indeksowane w obrębie bloku, podobnie jak bloki
wątków są indeksowane w obrębie gridów.
Blok (1,0,0) Blok (2,0,0) Blok (3,0,0)
Blok (1,1,0) Blok (2,1,0) Blok (3,1,0)
Grid (1,0,0)
Blok (2,1,0)
Wątek (1,0,0) Wątek (2,0,0) Wątek (3,0,0) Wątek (4,0,0)
Wątek (1,1,0) Wątek (2,1,0) Wątek (3,1,0) Wątek (4,1,0)
Współdzielona pamięć bloku
Wątek (2,1,0)
Prywatna pamięćlokalna wątku
Pamięć globalna przydzielona dla aplikacji
Rys. 1. Hierarchia wątków i pamięci w CUDA
Środowisko CUDA
4
Układy zbudowane zgodnie z architekturą CUDA bazują na mikroprocesorach
strumieniowych (ang. stream multiprocessor – SM) złożonych z kilku procesorów
strumieniowych (ang. stream procesor – SP) nazywanych także rdzeniami CUDA. Za
dystrybucję bloków wątków pomiędzy SM odpowiada globalny planer – GigaThread. Aby
obsłużyć setki wątków układy SM wykorzystują architekturę SIMT (ang. single instruction,
multiple thread), czyli pojedyncza instrukcja – wiele wątków. Zgodnie z nią wątki w celu
wykonania są grupowane w 32-elementowe grupy nazywane warpami [Nickolls i inni, 2008].
Poszczególne modele układów GPU różnią się między sobą przede wszystkim pod względem
liczby rdzeni, co wpływa na przebieg obliczeń. Korzystając z CUDA programista nie musi
znać ich liczby ponieważ wbudowane mechanizmy podzielą wątki zawarte w gridzie
pomiędzy dostępne rdzenie. Jednak aby uzyskać maksymalną wydajność konieczne jest
rozważenie rozmiaru bloków w zależności od architektury GPU. W konkretnych
zastosowaniach trzeba brać także pod uwagę inne ograniczenia, jak chociażby liczba
rejestrów dostępnych w obrębie jednego SM.
CPU
KopiowanieRAM CPU RAM GPU
KopiowanieRAM CPU RAM GPU
CPU
WY
KO
NA
NIE
PR
OG
RA
MU
GPU
Rys. 2. Schemat wykonywania programu zawierającego kod dla GPU
Schematycznie proces wykonywania programu zawierającego kod dla GPU został
przedstawiony na rysunku 2. Układy umożliwiające wykonywanie obliczeń GPGPU stanowią
urządzenia zewnętrzne względem CPU. Układy GPU stanowią w tym przypadku koprocesor
dla CPU umożliwiający uruchomienie wielu wątków równocześnie. W konsekwencji każde
uruchomienie programu na GPU musi zostać przeprowadzone przez CPU. W obu typach
Problem prosty w tomografii elektrycznej
5
układów występują moduły bezpośredniego dostępu do pamięci (ang. Direct Memory Access
- DMA), jednak w GPU są one bardziej rozbudowane. Należy też zauważyć, że układ CPU
korzysta z pamięci operacyjnej komputera, podczas gdy układ GPU musi się zadowolić
pamięcią umieszczoną na karcie na której jest zamontowany. Pamięć na karcie jest szybsza od
pamięci operacyjnej komputera, ale jest też mniejsza i nie można jej rozbudować. Taka
architektura implikuje konieczność przekopiowania do pamięci karty danych, na których będą
wykonywane obliczenia, a po zakończeniu obliczeń, wyników z pamięci karty do pamięci
operacyjnej komputera, co wydłuża czas całej operacji.
3. Problem prosty w tomografii elektrycznej
Głównym elementem systemu elektrycznej tomografii pojemnościowej jest czujnik
składający się z elektrod umieszczonych na obwodzie zbiornika, w którym przebiega proces.
Czujnik ten jest podłączony do układu elektronicznego, który mierzy pojemności pomiędzy
poszczególnymi parami elektrod, co zostało przedstawione schematycznie na rysunku 3.
Rys. 3. Schemat pomiaru w ECT dla czujnika pomiarowego z 12-ma elektrodami:
φ – potencjał elektryczny, V – wartość potencjału przyłożonego do elektrody pomiarowej, Cs,r – pojemność pomiędzy elektrodami s oraz r
Zmierzona pojemność elektryczna C jest wprost proporcjonalnie zależna od stałej
względnej przenikalności elektrycznej r zgodnie z równaniem 1:
grkC 0 (1)
gdzie 0 to przenikalność elektryczna próżni (8.85 pF·m-1
), a kg to współczynnik
geometryczny zależny od powierzchni elektrod i odległości między nimi. Po zebraniu
zestawu pomiarów pomiędzy wszystkimi elektrodami rozkład przenikalności elektrycznej
Problem prosty w tomografii elektrycznej
6
wewnątrz czujnika można uzyskać na drodze rekonstrukcji obrazu bazującej na rozwiązaniu
problemu odwrotnego dla równania 2:
r
dyxyxC r
rs
sr ),(),(1
0
(2)
W powyższym równaniu indeks r dotyczy elektrody odbiorczej, indeks s elektrody
nadawczej, to potencjał elektryczny, a Ωr to obszar całkowania na elektrodzie odbiorczej.
Po dyskretyzowaniu obszaru czujnika poprzez podzielenie go na N części równanie 2
można uprościć do formy liniowej, która przyjmie postać jak w równaniu 3 [Sankowski
i Sikora, 2010]:
11
NNMMεJC (3)
gdzie:
C – wektor zawierających M pomiarów pojemności
J – Jakobian reprezentujący zestaw map czułości dla wszystkich par elektrod
M – liczba pomiarów (par elektrod)
N – liczba części na które został podzielony obszar czujnika tomografu
– wektor zawierający wartości przenikalności elektrycznej we wszystkich częściach obszaru
czujnika tomografu
Rekonstrukcja obrazu dla elektrycznej tomografii pojemnościowej polega w tym
przypadku na znalezieniu wektora przy znanym wektorze C. Zagadnienie to jest nazywane
problemem odwrotnym. Problem ten jest niedookreślony (liczba danych M jest dużo
mniejsza od szukanych N) oraz źle postawiony [Lionheart, 2004]. Zauważyć należy, że
ponieważ M << N to nie jest możliwe obliczenie J-1
, a więc nie istnieje rozwiązanie liniowe.
Możliwe jest natomiast znalezienie rozwiązania problemu prostego, które polega na
znalezieniu wektora C przy znanym [Soleimani i inni, 2005].
Rozwiązanie problemu prostego metodą elementów skończonych należy rozpocząć od
podziału obszaru czujnika tomografu na N podobszarów opartych na K węzłach. Funkcję
rozkładu potencjału φ(x,y) można wtedy opisać z wykorzystaniem funkcji kształtu, zależnej
od kształtu elementu służącego do podziału obszaru czujnika, jako funkcji wartości potencjału
w węzłach siatki [Bolkowski i inni, 1993]. Rozkład potencjału elektrycznego wewnątrz
obszaru tomografu Ω w dwuwymiarowej płaszczyźnie XY, ograniczonego krzywą
zamkniętą jest opisany przez równanie Laplace’a [Sikora, 1998]:
0)]),([),(( yxgradyxdiv (4)
Problem prosty w tomografii elektrycznej
7
Przy poszukiwaniu rozwiązania problemu prostego należy zastosować warunki brzegowe
Dirichleta w punktach należących do elektrod zgodnie ze wzorem 5 oraz warunki Neumana
na pozostałej części brzegu [Bolkowski i inni, 1993].
r
s
yxdla
yxdlaVyx
,0
,),(
0 (5)
gdzie:
s – indeks elektrody nadawczej
r – indeks elektrody odbiorczej
V0 – potencjał przyłożony do elektrody nadawczej
Rozwiązanie równania Laplace’a z równania 4 można znaleźć poprzez szukanie
minimum wyrażenia funkcjonalnego przedstawionego przez równanie 6, które obrazuje
energię pola elektrycznego zgromadzoną wewnątrz czujnika tomografu [Bolkowski i inni,
1993].
dxdyyxgradyxyxW 2)]),([)(,(2
1)),(I( (6)
gdzie:
W – energia pola elektrycznego zgromadzona wewnątrz czujnika tomografu
Ω – powierzchnia tomografu
Szukanie minimum wartości funkcjonału z równania 6, można sprowadzić do
znalezienia punktu stacjonarnego (ekstremum) ze względu na wektor wartości węzłowych
szukanego rozkładu potencjału Φ [Bolkowski i inni, 1993]:
0I
I
I 1
K
(7)
Mając jednak na uwadze warunki brzegowe należy przyjąć ogólną postać równania
liniowego Metody Elementów Skończonych jak we wzorze 8:
BA
I (1)
gdzie elementy macierzy A stanowią współczynniki równania zależne od funkcji kształtu.
Algorytm rozwiązywania problemu prostego dla GPU
8
Powyższy układ równań należy rozwiązać dla każdej elektrody nadawczej, ponieważ
dla każdej z nich występują inne warunki brzegowe. Znając rozkład potencjału elektrycznego
dla poszczególnych elektrod nadawczych oraz rozkład przenikalności elektrycznej można
obliczyć pojemności pomiędzy każdą parą elektrod korzystając z zależności przedstawionej
na równaniu 2 ograniczając obszar całkowania do obszaru elektrody odbiorczej [Wajman
i inni, 2004].
4. Algorytm rozwiązywania problemu prostego dla GPU
Rozwiązanie problemu prostego opiera się w głównej mierze na rozwiązaniu układu
równań ze wzoru 8, czyli znalezieniu rozkładu potencjałów w obszarze czujnika tomografu.
Przy stosowanych rozmiarach siatek, a w konsekwencji rozmiarach macierzy A (rozmiar
macierzy to K x K gdzie K to liczba węzłów zastosowanej siatki), jest to proces czasochłonny
przy wykorzystaniu pojedynczego układu CPU. Należy więc poszukać metody, która pozwoli
na możliwie szybkie rozwiązanie tego układu równań. Metody rozwiazywania numerycznego
układów równań dzielą się na dwie główne grupy [Fortuna i inni, 1998]:
metody dokładne – które po wykonaniu skończonej liczby działań arytmetycznych
pozwalają otrzymać rozwiązanie np. metoda eliminacji Gaussa, metoda eliminacji
Jordana, metoda Cholesky’ego-Banachiewicza;
metody iteracyjne – które w każdej iteracji zwracają kolejne przybliżenia rozwiązania
np. metoda Jacobiego, metoda Gaussa-Seidla, metoda Czebyszewa.
W rozważanym przypadku można zauważyć, że macierz A jest macierzą rzadką,
jednak jej struktura zależy od zastosowanej do podziału obszaru czujnika siatki,
co uniemożliwia wyprowadzenie stałych uproszczonych wzorów na rozwiązanie układu
równań. Macierz A jest także macierzą pasmową, symetryczną względem głównej przekątnej.
Można także wykazać, że macierz ta jest dodatnio określona, co pozwala zastosować metodę
Cholesky’ego – Banachiewicza do rozkładu tej macierzy na iloczyn dwóch macierzy
trójkątnych [Bolkowski i inni, 1993, Fortuna i inni, 1998]:
(9)
przy czym macierz L jest macierzą trójkątną dolną z elementami na diagonali niekoniecznie
równymi 1. Rozwiązanie układu równań sprowadzi się wtedy do rozwiązania dwóch układów
równań z macierzami trójkątnymi:
Algorytm rozwiązywania problemu prostego dla GPU
9
(10)
(11)
O ile do rozwiązania układu równań z macierzą trójkątną przy pomocy układu GPU
można wykorzystać dostarczoną w bibliotece CUBLAS odpowiednią funkcję, to problemem
jaki należy rozwiązać jest rozkład macierzy A na macierze L oraz LT. Zgodnie z definicją
metody Cholesky’ego – Banachiewicza obliczenia należy przeprowadzać kolejno zgodnie ze
wzorami [Fortuna i inni, 1998]:
√ ∑
(12)
( ∑
)
(13)
gdzie:
Lii – element macierzy L w i-tym rzędzie i i-tej kolumnie
Aii – element macierzy A w i-tym rzędzie i i-tej kolumnie
Lji – element macierzy L w j-tym rzędzie i i-tej kolumnie
Aji – element macierzy A w j-tym rzędzie i i-tej kolumnie
K – liczba wierszy (liczba kolumn) macierzy A
W celu zaoszczędzenia miejsca w pamięci operacyjnej RAM, elementy macierzy A
zostają zastąpione elementami macierzy L, w taki sposób że po zakończeniu obliczeń dolna
trójkątna część macierzy A odpowiada dolnej trójkątnej części macierzy L.
W przedstawionych fragmentach kodu przyjęto kolejność zapisywania w pamięci komputera
elementów macierzy wierszami. Przykład kodu w języku C++ implementującego tę metodę
rozkładu Cholesky’ego-Banachiewicza został przedstawiony na listingu 1.
1 for(unsigned i=0; i<size; i++)
2 {
3 float S = 0;
4 for(unsigned k=0; k<i; k++)
5 S+=pow(aA[i*size+k],2);
6
7 aA[i*size+i] = sqrt(aA[i*size+i] - S);
8
9 for(unsigned j=i+1; j<size; j++)
10 {
11 S = 0;
12 for(unsigned k=0; k<i; k++)
13 S+= aA[j*size+k] * aA[i*size+k];
14
15 aA[j*size+i]=(aA[j*size+i]-S) / aA[i*size+i];
16 }
17 }
Listing 1. Implementacja metody rozkładu Cholesky’ego – Banachiewicza dla układu CPU
Algorytm rozwiązywania problemu prostego dla GPU
10
Powyższa implementacja sprawdza się dobrze w przypadku wykonywania przez jeden
wątek w układzie CPU, jednak skutkuje bardzo ograniczonymi możliwościami
w zakresie równoległego wykonania operacji, ponieważ musi być zachowana kolejność
wykonywania operacji arytmetycznych. Aby zwiększyć możliwości zrównoleglenia obliczeń
w tej metodzie należy zamienić kolejnością pętlę z linii 1 z pętlami z linii 4 i 12 listingu 1.
W efekcie kod w języku C++ przyjmie następującą postać:
1 for(unsigned k=0; k<size; k++)
2 { //krok 1
3 aA[k*size+k] = sqrt(aA[k*size+k]);
4 //krok2
5 for(unsigned j=k+1; j<size; j++)
6 aA[j*size+k] = aA[j*size+k] / aA[k*size+k];
7 //krok3
8 for(unsigned i=k+1; i<size; i++)
9 for(unsigned j=i; j<size; j++)
10 aA[j*size+i] -= aA[i*size+k] * aA[j*size+k];
11 }
Listing 2. Implementacja metody rozkładu Cholesky’ego – Banachiewicza dla układu CPU
po zamianie kolejności pętli
Pomimo zwiększenia stopnia skomplikowania obliczeń, uzyskano większą
niezależność wykonywanych operacji. W efekcie operacje w linii 6 mogą być wykonane
równolegle dla wszystkich elementów j z pętli z linii 5 (krok 2), a także operacje w linii 10
mogą być wykonane równolegle dla wszystkich wartości i oraz j z obu pętli z linii 8 oraz 9
(krok 3) listingu 2. Kolejność wykonywania obliczeń dla kolejnych wartości k (pętla z linii 1)
musi zostać zachowana, podobnie jak kolejność operacji w poszczególnych krokach.
Implementacja algorytmu z listingu 2 dla układów GPU byłaby jednak mało wydajna
ze względu na dużą ilość operacji odczytu pamięci globalnej w stosunku do operacji
arytmetycznych. Deklarowana przez producenta wydajność dla karty NVIDIA Tesla C1060
wynosi maksymalnie 933 GFLOPs dla operacji na liczbach o pojedynczej precyzji podczas
gdy przepustowość pamięci wynosi maksymalnie 102GB/s. Dla operacji z linii 6 listingu 2 na
jedną operację dzielenia przypadają 3 operacje dostępu do pamięci (2 operacje odczytu oraz
1 operacja zapisu). Liczba o pojedynczej precyzji zajmuje w pamięci 4kB, tak więc
maksymalna prędkość z jaką mogą być dostarczone dane do układu obliczeniowego to
102/4/3 = 8,5 GB/s, przez co maksymalna teoretyczna wydajność spada do 8,5 GLOPS, czyli
ok. 100 razy mniej od maksymalnej deklarowanej przez producenta. Równie niekorzystna
sytuacja występuje w linii 10 listingu 2. Ponieważ w prezentowanym algorytmie te same
elementy macierzy odczytywane są wielokrotnie przez różne wątki, można ograniczyć liczbę
dostępów do pamięci globalnej karty z układem GPU (pamięć RAM GPU) poprzez ładowanie
danych seriami z pamięci globalnej do dużo szybszej pamięci współdzielonej w ramach
Algorytm rozwiązywania problemu prostego dla GPU
11
jednego bloku wątków. Przy takim podejściu wątki w ramach każdego z takich bloków muszą
operować na tych samych elementach macierzy. Przy podziale problemu na bloki należy
kierować się jak największą możliwością wykorzystania odczytanych danych w obrębie
jednego bloku dla zminimalizowania liczby odczytów pamięci. Z drugiej strony rozmiar
bloku oraz współdzielonej w jego ramach pamięci są ograniczone architekturą układu GPU,
który będzie wykorzystywany do obliczeń. Ponieważ wraz z nowymi układami GPU
dostępnymi na rynku ich możliwości są coraz większe, rozmiar bloku należy dobrać do
konkretnego układu GPU dla otrzymania maksymalnej wydajności. Na rysunku 4
przedstawiono schemat blokowy proponowanego algorytmu wykorzystującego podział
macierzy na bloki wspólnych obliczeń.
Rys. 4. Schemat blokowy algorytmu rozkładu Cholesky’ego – Banachiewicza z podziałem macierzy na bloki umożliwiające przeprowadzenie obliczeń w sposób równoległy.
Algorytm rozwiązywania problemu prostego dla GPU
12
Zaproponowany algorytm wykonuje obliczenia zawsze na prawej górnej części
macierzy przesuwając po każdym przebiegu pętli wskaźnik pierwszego bloku o jeden wiersz
i jedną kolumnę tj. po kolejnych blokach znajdujących się na diagonali. Algorytm kończy
działanie, gdy rozkład Choleksy’ego-Banachiewicza zostanie obliczony dla ostatniego bloku
na diagonali w dolnym prawym rogu.
Komentarza wymagają operacje wykonywane w każdym kroku algorytmu. W kroku
pierwszym pierwszy blok zostaje poddany klasycznej implementacji rozkładu
Cholesky’ego-Banchiewicza. Funkcja kernela, jest uruchamiana w gridzie składającym się
z jednego bloku o rozmiarach odpowiadających rozmiarowi bloku. W ten sposób każdy wątek
odpowiada za odczyt jednego elementu macierzy do tablicy we współdzielonej pamięci
bloku, a następnie za obliczenia dla jednego elementu macierzy. Na koniec każdy wątek
zapisuje wartość jednego elementu tablicy ze współdzielonej pamięci bloku do pamięci
globalnej. Synchronizacja pomiędzy wątkami jest niezbędna dla zagwarantowania,
że wszystkie elementy z pamięci globalnej zostały załadowane do tablicy w pamięci
współdzielonej. Synchronizacja zapewnia także, że etapy obliczeń dla wszystkich elementów
tablicy zostały zakończone przed przejściem do kolejnych etapów.
Drugi krok pętli składa się de facto z dwóch etapów, ale ponieważ wykorzystują one
te same dane wejściowe, dla ograniczenia liczby odczytów z pamięci globalnej są one
wykonywane w jednej funkcji kernela, a co za tym idzie przy jednym wywołaniu. Każdy
z wątków odpowiada za odczyt z pamięci globalnej do tablic w pamięci współdzielonej
w obrębie wątku jednego elementu z bloku na diagonali oraz jednego elementu z bloku
w pierwszej kolumnie pod diagonalą. Ponieważ funkcja kernela jest wywoływana
w jednowymiarowym gridzie składającym się z tylu bloków wątków ile bloków jest
w macierzy pod blokiem na diagonali, to każdy blok wątków będzie odpowiadał za inny blok
danych macierzy. Z kolei każdy blok wątków składa się z tylu wątków ile elementów jest
w bloku macierzy. Taki podział zapewnia, że operacje w poszczególnych blokach wątków
mogą być wykonane niezależnie od siebie. W pierwszym etapie funkcja kernela aktualizuje
dane w blokach pod blokiem na diagonali przesuwając się kolumna po kolumnie, następnie
aktualizuje dane w kolejnych blokach na diagonali, także przesuwając się kolumna po
kolumnie w obrębie każdego bloku.
Funkcja kernela dla trzeciego etapu obciąża układ GPU w największym stopniu. Jest
ona wywoływana w dwuwymiarowym kwadratowym gridzie, przy czym długość jak
i szerokość tego gridu są równe liczbie bloków w macierzy pod blokiem na diagonali.
Podobnie jak i w przypadku funkcji kernela dla pierwszego oraz drugiego etapu liczba
Algorytm rozwiązywania problemu prostego dla GPU
13
elementów w bloku wątków odpowiada liczbie elementów w bloku danych macierzy. Każdy
blok wątków odczytuje dwa bloki danych i aktualizuje jeden blok danych macierzy w pamięci
globalnej odpowiedni do umiejscowienia w macierzy odczytanych bloków danych. Operacje
odczytu są wykonywane dla wszystkich niepowtarzających się par bloków danych
znajdujących się w pierwszej kolumnie macierzy pod blokiem na diagonali. Dzięki takiemu
podziałowi operacje w każdym bloku wątków, jak i w pojedynczym wątku mogą być
wykonywane równolegle (pomijają synchronizację wątków w bloku po odczycie danych
z pamięci globalnej do tablic w pamięci współdzielonej bloku).
Zastosowanie zaproponowanego algorytmu rozkładu Cholesky’ego-Banachiewicza
wymusza, aby rozmiar macierzy A był wielokrotnością rozmiaru bloku. Rozwiązaniem tego
wymagania jest dopełnianie macierzy do rozmiaru będącego wielokrotnością rozmiaru bloku,
w taki sposób, że dodane elementy będą miały wartość 0, z wyjątkiem elementów na
diagonali które będą miały wartość 1. Takie podejście doprowadzi do zwiększenia liczby
obliczeń, jednak przy rozmiarze macierzy dużo większym od rozmiaru bloku, a takie
przypadki są rozważane, będzie to wzrost marginalny. Dodatkowo, przy założeniu że rozmiar
macierzy jest zawsze wielokrotnością rozmiaru bloku można zastosować rozwinięcie pętli for
w funkcjach kernela, czyli zastąpienie pętli kodem instrukcji wykonywanych w każdym
kroku pętli. Jest to znana praktyka w przypadku programowania dla układów GPU, która
prowadzi do lepszego wykorzystania ich zasobów [Sanders i Kandrot, 2010].
Przed przystąpieniem do rozwiązywania układu równań 8 konieczne jest jeszcze
wprowadzenie informacji o warunkach brzegowych. Jest to realizowane poprzez zastąpienie
prawej strony równania macierzą B’, której elementy są obliczane zgodnie ze wzorem (14):
ijKjdlaijijj ;,2,1 ABB (2)
gdzie:
i – indeks węzła należącego do elektrody
j – indeks węzła
K – liczba węzłów
Konieczne jest także usunięcie z macierzy B’ wierszy o indeksach odpowiadających
indeksom węzłów należącym do elektrod, a co za tym idzie także utworzenie macierzy A’
jako kopii macierzy A zredukowanej o wiersze oraz kolumny o indeksach odpowiadających
indeksom węzłów należących do elektrod [Sikora, 1998]. Choć proponowana metoda wiąże
się z większym zapotrzebowaniem na pamięć RAM oraz większą ilością czasu potrzebnego
na kopiowanie danych pomiędzy macierzami A - A’ oraz B - B’, to prowadzi do znacznej
Algorytm rozwiązywania problemu prostego dla GPU
14
redukcji liczby elementów w układzie równań. Po otrzymaniu rozwiązania dla tak
zmodyfikowanego układu równań konieczne jest uzupełnienie go o wartości potencjałów
w węzłach należących do elektrod. W opracowanej implementacji tego algorytmu
zdecydowano się na obliczenie macierzy A’ oraz B’ jeszcze przed przekopiowaniem ich do
pamięci RAM układu GPU. Decyzja taka była umotywowana krótszym czasem wykonywania
tej operacji przez układ CPU niż w przypadku wykonywania jej w pamięci RAM układu
GPU, czy też w trakcie kopiowania do pamięci RAM układu GPU. Podsumowując wszystkie
powyższe rozważania, proponowany algorytm rozwiązywania problemu prostego przyjął
postać zaprezentowaną jako schemat blokowy na rysunku 5.
Rys. 5. Schemat blokowy algorytmu rozwiązywania problemu prostego dla ECT
Algorytm rozwiązywania problemu prostego dla GPU
15
W celu porównania wydajności proponowanego algorytmu dla GPU oraz algorytmu
dla CPU, próby wydajnościowe algorytmów rozwiązywania problemu w ECT na komputerze,
którego dokładna specyfikacja jest przedstawiona w tabeli 1. Ze względu na znaczne rozmiary
macierzy A, część obliczeń przeznaczona dla układu GPU była przeprowadzona na karcie
NVIDIA Tesla C1060. Karta ta pomimo braku wyjścia służącego do podłączenia monitora
została wyposażona w 4 GB pamięci RAM, czyli 4 razy więcej niż znajdująca się na
wyposażeniu używanego komputera karta graficzna NVIDIA Geforce GTX 285. Ponieważ
czas potrzebny na wykonanie obliczeń na liczbach zmiennoprzecinkowych o podwójnej
precyzji (double) dla układów GPU z compute capability 1.3, w jakie było wyposażone
stanowisko badawcze, jest ok. 10-krotnie dłuższy niż w przypadku tych samych operacji
wykonanych przy pojedynczej precyzji, wszystkie obliczenia były wykonywane na liczbach
zmiennoprzecinkowych o pojedynczej precyzji (float lub single). Należy jednak wspomnieć,
że w przypadku nowszych układów GPU firmy NVIDIA z compute capabilty 2.0 czas
potrzebny na wykonanie operacji na liczbach zmiennoprzecinkowych o podwójnej precyzji
jest jedynie 2-krotnie dłuższy niż dla liczb zmiennoprzecinkowych o pojedynczej precyzji.
Dla tych układów zaleca się wykonywanie opisanych algorytmów przy zachowaniu
podwójnej precyzji, dla maksymalnego ograniczenia błędów numerycznych.
Tabela 1. Szczegółowa konfiguracja wykorzystywanego komputera
Element Opis
procesor Intel Core i7-920
płyta główna Gigabyte EX58-UD5 (Intel X58+ICH10R)
pamięć operacyjna RAM Patriot 1600LL 6GB (2x3GB) PC3-12800 DDR3
dysk twardy Samsung HE103UJ (1TB, 32MB cache, SATA)
karta graficzna NVIDIA GeForce GTX285 (GT200) 1GB GDDR3
compute capability karty graficznej 1.3
karta do obliczeń GPU NVIDIA Tesla C1060 (GT200) 4 GB GDDR3
compute capability karty do obliczeń GPU 1.3
system operacyjny Windows XP Professional x64
środowisko programistyczne Visual Studio 2008
W porównaniach wzięto pod uwagę obie metody wprowadzania warunków
brzegowych, tzn. wersję bez zmiany warunków brzegowych oraz wersję zmieniającą rozmiar
macierzy A poprzez usunięcie wierszy oraz kolumn odpowiadających węzłom należącym do
elektrod. Próby te przeprowadzono dla czujników składających się z 8 oraz 16 elektrod przy
założeniu, że 2% wszystkich węzłów należy do elektrod. Wykresy prezentujące zależności
czasu wykonywania obliczeń poszczególnych algorytmów od liczby węzłów siatki
przedstawiono na rysunkach 6 oraz 7. Liczba węzłów siatki odpowiada w tym przypadku
Algorytm rozwiązywania problemu prostego dla GPU
16
liczbie wierszy (macierz jest kwadratowa więc liczba wierszy jest taka sama jak kolumn)
macierzy A przed wprowadzeniem warunków brzegowych.
Rys. 6. Wykres prezentujący zależność czasu wykonywania obliczeń przez poszczególne algorytmy
rozwiązywania problemu prostego od liczby węzłów siatki, dla czujnika zawierającego 8 elektrod.
Rys. 7. Wykres prezentujący zależność czasu wykonywania obliczeń przez poszczególne algorytmy
rozwiązywania problemu prostego od liczby węzłów siatki, dla czujnika zawierającego 16 elektrod.
0
50000
100000
150000
200000
250000
0 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
czas
(ms)
liczba węzłów siatki
8 elektrod
CPU pełne A
CPU przycięte A
GPU pełne A
GPU przycięte A
0
50000
100000
150000
200000
250000
0 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
czas
(ms)
liczba węzłów siatki
16 elektrod
CPU pełne A
CPU przycięte A
GPU pełne A
GPU przycięte A
Podsumowanie
17
Powyższe wykresy ilustrują około 7,5-krotne skrócenie czasu rozwiązywania
problemu prostego przez zaproponowany przez autora algorytm dla układu GPU w przypadku
czujnika z 8 elektrodami, a także około 8,2 krotne w przypadku czujnika z 16 elektrodami.
Można także zauważyć, że zarówno dla układu CPU, jak i układu GPU metoda wprowadzania
warunków brzegowych poprzez ograniczenie rozmiaru macierzy A pozwala skrócić o około
5% czas potrzebny na wykonanie obliczeń dla czujnika z 8 elektrodami oraz o około 10%
czasu dla czujnika z 16 elektrodami. Płynie z tego wniosek, że w przypadku gdy
dysponujemy dostatecznie dużą ilością pamięci operacyjnej aby dokonać redukcji macierzy A
warto rozważyć zastosowanie tej metody.
5. Podsumowanie
W niniejszej pracy przedstawiono pionierskie w skali światowej prace badawcze nad
zastosowaniem procesorów graficznych (GPU) do obliczeń równoległych dla potrzeb
tomografii procesowej. Celem tych prac było opracowanie algorytmów przetwarzania
tomograficznych danych pomiarowych z wykorzystaniem procesorów graficznych w celu
przyspieszenia obliczeń i w efekcie uzyskania wyższej rozdzielczości czasowo-przestrzennej
rekonstruowanych obrazów.
Najistotniejszym elementem przedstawionym w niniejszej pracy jest nowatorski
algorytm rozwiązywania problemu prostego w systemach ECT oparty na autorskiej metodzie
rozkładu macierzy współczynników równania liniowego na macierze L oraz LT. Algorytm
dekompozycji macierzy bazuje na metodzie Cholesky’ego-Banchiewicza. O ile metoda
Cholesky’ego-Banachiewicza jest typowym algorytmem, który można wykonać wyłącznie
sekwencyjnie, to opracowany przez autora algorytm dekompozycji macierzy umożliwia
wykonanie większości operacji arytmetycznych równolegle. Pozwoliło to na użycie układów
GPU przetwarzania danych pomiarowych z przemysłowych systemów tomograficznych.
Przeprowadzone próby wydajnościowe dowiodły, że użycie zaproponowanego
algorytmu dla układów GPU pozwoliło uzyskać ponad 8-krotne skrócenie czasu
przetwarzania danych pomiarowych dla ECT. Tak znaczące przyśpieszenie obliczeń może
zostać wykorzystane w przemysłowych systemach tomograficznych w celu:
1) zwiększenia rozdzielczości przestrzennej obrazów,
2) uzyskania większej liczby obrazów w jednostce czasu (rozdzielczości czasowej),
3) zwiększenia rozdzielczości czasowo-przestrzennej obrazów.
Podziękowania
18
Daje to możliwość szerszego zastosowania tomografii procesowej w systemach
monitorowania dynamicznych procesów przemysłowych. Jest to szczególnie istotne
w przypadku procesów monitorowanych z użyciem tomografii procesowej, w których czas
przetwarzania danych był dotychczas zbyt długi, by wyniki tego przetwarzania mogły zostać
użyte do sterowania procesem. Nie bez znaczenia jest również stosunkowo niska cena oraz
niewielkie rozmiary kart z układami GPU w porównaniu do rozwiązań dostarczających
podobną moc obliczeniową, ale w oparciu o układy CPU. To wszystko powinno przełożyć się
na rosnące zainteresowanie ze strony przemysłu, a w konsekwencji do upowszechnienia się
procesorów graficznych w zastosowaniach do obliczeń w diagnostycznych systemach
tomograficznych.
Doświadczenia zebrane w trakcie prowadzonych przez autora prac badawczych
wskazują na dużą zależność wydajności proponowanego algorytmu od wykorzystywanej
platformy sprzętowej z układami GPU. Autor proponuje przeprowadzenie dalszych prac
badawczych w kierunku poszukiwania metod modyfikujących działanie algorytmów
w zależności od możliwości układu GPU, na którym wykonywane są obliczenia. Możliwe jest
także przeprowadzenie adaptacji zaproponowanego algorytmu w celu uruchomienia go
w środowisku złożonym z wielu układów GPU. Powinno to przynieść korzyść w postaci
dalszego skrócenia czasu obliczeń, jednak pociągnie za sobą konieczność uwzględnienia
kolejnych ograniczeń, głównie w zakresie transferu danych.
6. Podziękowania
Komputer, który umożliwił opracowanie algorytmów oraz przeprowadzenie opisanych
testów został zakupiony w ramach uzyskanego przez autora stypendium celowego na
dofinansowanie prac naukowo-badawczych dla doktorantów w ramach projektu pt.:
„Stypendia wspierające innowacyjne badania naukowe doktorantów” (Projekt finansowany ze
środków Unii Europejskiej z Europejskiego Funduszu Społecznego oraz budżetu państwa
w ramach Zintegrowanego Programu Operacyjnego Rozwoju Regionalnego). Autor był także
stypendystą w ramach projektu „Innowacyjna dydaktyka bez ograniczeń - zintegrowany
rozwój Politechniki Łódzkiej - zarządzanie uczelnią, nowoczesna oferta edukacyjna
i wzmacnianie zdolności do zatrudniania, także osób niepełnosprawnych” (Projekt
współfinansowany przez Unię Europejską w ramach Europejskiego Funduszu Społecznego).
Literatura
19
7. Literatura
[1] Bolkowski S., Stabrowski M., Sroka J., Sikora J.: Komputerowe metody analizy pola
elektromagnetycznego, wyd. II, WN-T, Warszawa, 1993.
[2] Fortuna Z., Macukow B., Wąsowski J.: Metody numeryczne, wyd. czwarte,
Wydawnictwo Naukowo-Techniczne, Warszawa, 1998.
[3] Harris M.: Real-Time Cloud Simulation and Rendering, Ph. D. dissertation, University
of North Carolina, 2003.
[4] Ikeda T., Ino F., Hagihara K.: A code motion technique for accelerating general-
purpose computation on the GPU, 20th International Parallel and Distributed
Processing Symposium, 2006.
[5] Kirk D.: NVIDIA CUDA software and GPU parallel computing architecture,
Proceedings - International Symposium on Memory Management, 2007, s. 103.
[6] Lindholm E., Nickolls J., Oberman S., Montrym J.: NVIDIA Tesla: A Unified Graphics
and Computing Architecture, IEEE Micro, 28(2), 2008, s. 39-55.
[7] Lionheart W. R. B.,: Review: Developments in EIT reconstruction algorithms: pitfalls,
challenges and recent developments, Physiol. Meas., 25, 2004, s. 125-142.
[8] Nickolls J., Buck I, Garland M., Skadron K.: Scalable Parallel Programming, ACM
Queue - GPU Computing, 6 (2), 2008, s. 40-53.
[9] NVIDIA CUDA C Programming Guide, wersja 4, 06.05.2011,
http://developer.nvidia.com
[10] Reinecke N., Mewes D.: Recent developments and industrial/research applications of
capacitance tomography, Meas. Sci. Technol., 7, 2006, s. 233 246.
[11] Sanders J., Kandrot E.: CUDA by Example, NVIDIA / Addison-Wesley (Pearson
Education), Second Printing, United States, 2010.
[12] Sankowski D. , Sikora J.: Electrical Capacitance Tomography: Theoretical Basis and
Applications, Wydawnictwo Książkowe Instytutu Elektrotechniki, Warszawa, 2010.
[13] Sikora J.: Algorytmu numeryczne w tomografii impedancyjnej, wyd. I, Oficyna
Wydawnicza Politechniki Warszawskiej, Warszawa, 1998.
[14] Soleimani M., Lionheart W. R. B., Byars M., Pendleton J., (2005): Nonlinear Image
Reconstruction of Electrical Capacitance Tomography (ECT) based on a Validated
Forward Model, 4th World Congress on Industrial Process Tomography, Japan, pp.
558-563.
[15] Stone J.E., Gohara D., Shi G.,(2010), OpenCL: A Parallel Programming Standard for
Heterogeneous Computing Systems, Computing in Science & Engineering, 12 (3), pp.
66 73.
[16] Verdiere G. C. de, (2011), Introduction to GPGPU, a hardware and software
background, Comptes Rendus Mecanique, 339 (2-3), High Performance Computing, pp.
78-89.
[17] Wajman R., Mazurkiewicz Ł, Sankowski D., (2004), Mapy czułości w procesie
rekonstrukcji obrazów dla elektrycznej tomografii pojemnościowej, Automatyka, tom 8,
zeszyt 3.