Nikházy László Ureczky Bálint Konzulens: dr. Horváth Gábor Orvosi képfeldolgozási algoritmusok párhuzamos megvalósítása CUDA technológiával Nikházy László Ureczky Bálint Konzulens: dr. Horváth Gábor
I. GPGPU és a CUDA
GPGPU General Purpose Computation on GPUs
CPU vs GPU
NVIDIA® CUDA™ GPGPU elősegítése Compute Unified Device Architecture általános programozhatóság shader-nyelvek számítógépes grafika skálázhatóság szál-hierarchia memória-hierarchia
A CUDA Architektúra
CUDA támogatottság Java Python, Fortran, … Windows / Linux / MacOS C/C++ C Runtime for CUDA .NET nyelvek CUDA.NET – http://www.gass- ltd.co.il/en/products/cuda.net Java JaCuda – http://jacuda.wiki.sourceforge.net Python, Fortran, … Windows / Linux / MacOS
Kernelfüggvények
Szálhierarchia
Memóriahierarchia
NVIDIA GeForce 9800GTX+ 16 multiprocesszor 8 szálprocesszor / MP Memória: 512MB GDDR3 256-bit busz 1100 MHz 70.4 GB/s 1.1-es számítási kapacitás
1.0 - ás számítási kapacitás: rácsdimenziók : 65535 , 65535 , 1 blokkdimenziók : 512 , 512 , 64 szál/proc : 512 konstans memória : 64KB MP : 8KB regiszter/MP : 8192 aktív blokk : 8 atív szál : 768 1.1 - es számítási kapacitás: 32-bites atomi függvények
II. CUDA programozás
Program felépítés A hoszton és az eszközön futó kód együtt C nyelvi kiterjesztések Szétválasztás fordítási időben Ajánlott fejlesztőkörnyezet: Microsoft Visual Studio
Nyelvi kiterjesztések - függvények __host__ a hoszton fut, csak a hosztról lehet hívni __device__ az eszközön fut, csak az eszközről hívható __global__ kernel függvény a párhuzamosítás eszköze az eszközön fut, csak a hosztról lehet hívni Pl.: deklaráció: __global__ void func(float* parameter); meghívás: func<<<dimGrid, dimBlock>>>(parameter);
Nyelvi kiterjesztések - változók __device__ az eszköz globális memóriájában foglal helyet __shared__ egy szál blokk közös memóriájában van csak a blokkban lévő szálakból érhető el __constant__ az eszköz konstans memóriaterületén helyezkedik el minden szálból, ill. a hosztról is elérhető Beépített változók (kernel függvényekben) gridDim, blockDim blockIdx, threadIdx
Példa – mátrix összeadás __global__ void matAdd(float A[N][N], float B[N][N], float C[N][N]) { int i = blockIdx.x * blockDim.x + threadIdx.x; int j = blockIdx.y * blockDim.y + threadIdx.y; if (i < N && j < N) C[i][j] = A[i][j] + B[i][j]; } int main() // kernel hivas dim3 dimBlock(16, 16); dim3 dimGrid((N + dimBlock.x – 1) / dimBlock.x, (N + dimBlock.y – 1) / dimBlock.y); matAdd<<<dimGrid, dimBlock>>>(A, B, C);
Memória menedzsment Textúra memória: cache-elt foglalás, felszabadítás: cudaMalloc(void** ptr, size_t nbytes); cudaFree(void* ptr); másolás: cudaMemcpy(void* dst, void* src, size_t nbytes, enum cudaMemcpyKind direction);
Szinkronizáció Szálak között Hoszt - eszköz kernel hívás aszinkron memória másolás szinkron Szálak között ha több szál írja/olvassa ugyanazt a memóriaterületet, a sorrend nem determinisztikus megoldás: __syncthreads(); szinkronizációs pont: csak akkor halad tovább a végrehajtás, ha a blokkban minden szál eljutott eddig az utasításig
CUDA program általános menete Adatok beolvasása a hoszton Adatok másolása a hosztról az eszköz globális memóriájába Kernel függvény hívása adatok másolása a globális memóriából a közös memóriába számítások párhuzamos eredmények visszaírása a közös memóriából a globális memóriába Eredmény másolása az eszközről a hoszt memóriájába Eredmény kiírása
III Párhuzamosan megvalósított algoritmusok
Kulcscsontkeresés
Kulcscsontkeresés Előfeldolgozás (gyors) Radon transzformáció (~1-2 mp) kulcscsont főtengelyének meghatározása Snake algoritmus (~2-3 mp): Kulcscsont körvonalának iteratív meghatározása Kulcscsont-eltűntetés…
6 5 5 4 3 2 1 Előfeldolgozás Bejövő kép Gauss szűrés Gradiens számítás Bejövő képpel pixelenkénti szorzás Kulcscsont iránya szerinti szurés Maximális értékek „elfojtása” Kimeneti kép 6 5 5 4 3 2 1
Radon-transzformáció (2D-s, vonal szerinti Randon transzformáció) 1 -1 -90° -45° 0° 45° 90°
Mátrix konvolúció
Párhuzamos megvalósítás Az eredmény mátrix minden egyes eleméhez külön szál Egy szálon belül az összeg kiszámítása for ciklussal Blokk méret: 16x16 Textúra memória használata
Tesztelés: Prewitt operátor
FFT DFT: Radix-2 FFT: A A+B A-B B
Butterfly diagram W80 W80 W82 W80 W80 W81 W80 W82 W80 W83 W80 W82 x(0)
Párhuzamos megvalósítás Annyi szál, ahány pillangó egy szinten (N/2) Minden iterációban minden szál egy pillangó műveletet végez Közös memória használata __syncthreads() iterációnként 2D FFT: 1D FFT-k sorozata horizontálisan, majd vertikálisan
Teszt
Kitekintés 100-szoros gyorsítás az elvi határ Számításintenzív feladatoknál (ahol sok egyszerű számolási műveletet végzünk egy adattal) ez jó közelítéssel elérhető Memóriaintenzív feladatoknál 20-30-szoros gyorsítás reális A tüdőkörvonal meghatározás inkább az utóbbi jellegű