Párhuzamosítás - mit.bme.huhorvath/KD/2015osz/Orvosi kepdiagnosztika...Párhuzamosítás...
-
Upload
trinhduong -
Category
Documents
-
view
219 -
download
0
Transcript of Párhuzamosítás - mit.bme.huhorvath/KD/2015osz/Orvosi kepdiagnosztika...Párhuzamosítás...
Párhuzamosítás
• Algoritmikus optimalizáció > párhuzamosítás – Soha sincs végtelen feldolgozóegység
• Legjobb esetben lineáris gyorsulás – Függőségek, közös erőforrások (memória sávszélesség,
hálózat, ...) miatt általában ennél kevesebb
• Mekkora lineáris gyorsulás? – 1 szál helyett akár 4096 16 szálat futtató mag = 65536-
szoros gyorsulás • 1 nap = 86400s azaz ami egy napig futott az akár 1s lehet
• Annyira mégse rossz
2
Párhuzamosítás
• Van órajel is, ami viszont általában fele vagy negyede – Így se rossz az 5 másodperc egy nap helyett
• Mikor lehet párhuzamosítani? – Nem triviális
– Akkor biztosan nem lehet, ha mindig meg kell várni az előző lépés eredményét.
– Képeknél általában jól lehet, mert egy kimeneti pixel csak a bemeneti képtől szokott függeni
3
Műveletek képeknél
• Pixel manipulációk (két kép összeadása, intenzitás szorzása,...) – Triviálisan nagyon jól párhuzamosítható
• Minimum, maximum, összeg, normalizálás az egész képre, FFT – Parallel reduction
• Szűrések – Minden pixel a saját környezettől függ
– A triviális megoldás is gyorsít • Szeparábilis szűrők
4
Műveletek képeknél
• Szűrések (folytatás) – GPU-n érdemes a nem triviális megoldást
választani
– Nem konvolúciós szűrők • Mediánnál pl. a rendezés nem megy egy lépésben
• A morfológia is ilyen! De az könnyen számolható
• Szegmentáció, flood fill – A már meglévő eredményt felhasználjuk a további
iterációkban. • Azért itt is jelentősen lehet gyorsítani
5
Műveletek képeknél
• Rekonstrukció, Röntgen szimuláció, Vizualizáció
– „Mindenki mindenkitől” függ kis túlzással
– Több iteráció
– Egy iteráción belül a kimeneti pixelek külön számolhatóak
• Optical flow, regisztráció
– Ezek se lokális algoritmusok
• Hisztogram
– Sok száll írja a kimenetet -> atomi műveletek
6
Párhuzamosság
• Statikus párhuzamosság
– Az elején eldöntjük, hogy hány szál fusson
– Pl. két kép összeadása
– Aktív szálak száma így is változhat
• Flood fill
• Dinamikus párhuzamosság
– Menet közben növekedhet a szálak száma
– Pl. ray tracing
7
Megkötések
• Nem minden eszköz tud dinamikus párhuzamosságot
• Lehetnek feldolgozóegységek amelyek nem képesek független műveletet végrehajtani – SIMD
• MMX, SSE, AVX
• GPU Multiprocesszorok
• Közös erőforrások elosztása – Leggyakrabban a memória sávszélesség a kritikus
8
Megkötések
• SMP vs NUMA
– SMP - Symmetric multiprocessing
• Minden feldolgozóegység ugyanúgy fér hozzá az egész memóriához
• GPU ilyen
– De itt is jelentősen befolyásolja a teljesítményt, hogy az egymást követő szálak egymást követő memóriacellákat érnek-e el.
– NUMA - Non-uniform Memory Access
• Új Intel CPU-k ilyenek, a magokhoz tartoznak memóriák
• Többprocesszoros rendszereknél akár igen nagy költsége is lehet egy távolabbi egységhez tartozó memória elérésének
9
Hátrányok
• Olvashatatlan lesz a kód ha nagyon optimalizáljuk
• Nehéz debuggolni
– Szerencsére azért már vannak eszközök GPU-ra is de nem olyan jók, mint a CPU-sak.
– Több szálat CPU-n is nehezebb debuggolni
• Új nyelveket, API-kat kell megtanulni
• Nem hordozható a kód
– Vagy legalábbis elveszítjük az előnyöket ha nagyon optimalizálva volt
10
Eszközök
• CPU – Több szál indítása – OpenMP – Vektorizáció
• Intel: MMX, SSE (2,3,SSSE3,4.1,4.2), AVX, AVX2 – Xeon: AVX-512
• ARM: Neon
• GPU – OpenCL, CUDA, GLSL/HLSL
• Gyorsító kártyák – Intel Xeon Phi – FPGA
• Szabad több gépet is használni
11
CPU
• Előnyök – Az egész memóriát elérjük és sok memóriánk van – Akár 64Gb memória hagyományos asztali gépben – Jól debuggolható – Könnyen kezelhető a már valószínűleg ismert
eszköztárral – Könnyű szinkronizáció – Magas teljesítmény szálanként – Az adatokat nem kell fel-/letölteni
• Hátrányok – Nem annyira párhuzamos, mint a többi megoldás
12
GPU
• Előnyök
– Nagyon párhuzamos
– Elérhető ár, jó ár/teljesítmény arány
– Jó FLOPS/Watt arány
• Hátrányok
– Külön nyelvet kell megtanulni
– Nagyon specifikus optimalizáció
– Külön memória, fel-/letöltés
13
Gyorsító kártyák
• Előnyök
– Speciális feladatokra lehet optimalizálni a hardwaret
• Hátrányok
– Drága
– Specializált, tehát nem hordozható és meg kell tanulni hozzá mindent
14
CPU Technológiák
• CPU
– Processek közötti kommunikáció
– _beginthread() / fork()
• Mutexek
• Események
– OpenMP
– Auto-vektorizáció
– Intrinsic
15
Processzek közötti kommunikáció
• Futtathatunk párhuzamosan több programot – Windowson és Linuxon is van lehetőség közös
memóriaterület létrehozására – Lehet rendszer szintű mutexekkel, eseményekkel
szinkronizálni
• Hasznos lehet két külön fejlesztett modul összekötésénél, több különböző kimenet előállításánál
• Pl. CAD szerver, 3D rekonstrukció, megjelenítő párhuzamosan futhat
• Az erőforrásokra nehezebb így figyelni
16
Szálak indítása
• Windows – _beginthread()/_beginthreadex()
• Windows.h
– Egy függvény a paramétere aminek átad egy void * paramétert
– Viszonylag nagy overhead az elindítás – Az ütemezés szálanként megy, így több processzoridőt
kapunk eleve – Szinkronizáció
• Mutex – Csak egy szál birtokolhatja – Akár rendszer szintű
• Critical section
17
Szálak indítása
• Windows – Szinkronizáció (folytatás)
• Események – Lehet várakozni rájuk
– Lehet triggerelni őket
• Linux/Unix – fork()
• A két szálon más a visszatérési értéke
– Nagyjából ugyan azok a szinkronizációs eszközök állnak rendelkezésre, mint Windowson
18
Szálak indítása
• Jó ugyanarra, mint a több processz indítása, csak nehezebb cserélni egy részét
• Hasznos ha egy folyamatnak több részét tudjuk párhuzamosan csinálni
– Pl. Pipeline program szervezés
• Következő kép betöltése/előfeldolgozása miközben az aktuálisat feldolgozzuk
19
OpenMP
• C/C++ #pragma
• Thread pool-okat használ
– Sokkal kisebb overhead egy szál indítása
• Nagyon kis módosítás a programkódban
• Párhuzamos for ciklusok
#pragma omp parallel for
for(int i = 0;i < 10000;i++)
{
A[i] = i;
}
20
OpenMP
• Be kell kapcsolni a fordítónál
– gcc: -fopenmp
– Visual Studio: C/C++ / Language / OpenMP support
• omp.h
– Függvények amivel a pragmák mellett megadhatunk paramétereket
• pl. A szálak számát
21
OpenMP blokkok
• Parallel blokkok
– Egy paragma egy párhuzamos blokkot hoz létre
– Egy blokkon belül nem lehet még egy párhuzamos blokk
• OpenMP 3.0-tól meg lehet oldani
– Több egymásba ágyazott for párhuzamosítása egy for ciklusra való átírással
22
OpenMP változók
• A szekción belül definiált változók privátok
– Minden szálnak saját példány
• A szekción kívül definiált változók shared-ek
– Egy közös változó az összes threadnek
• Explicit módon is meg lehet adni:
#pragma omp parallel for private(a, b) shared(c)
23
OpenMP redukciók
• Redukció amikor sok adatból lesz egy
– Szumma, átlag, szorzat, és kapcsolat,...
• Atomi műveletek kellenének
– Elfedi előlünk az OpenMP
#pragma omp parallel for reduction(+:sum)
24
OpenMP omp.h
• Vannak hasznos függvények:
– omp_get_thread_num()
– omp_get_num_threads()
– omp_set_num_threads()
• Nem garantált, hogy tényleg annyi lesz
• A legtöbb esetben nem szükségesek a függvények #pragma omp parallel for num_threads(4)
• Az OpenMP még sok mindenre képes, de az esetek 90%-ban ennyi elég
25
Auto-vektorizáció
• A fordítót meg lehet kérni, hogy for ciklusokat írjon át SIMD utasításokra
– Gcc –O3, Visual C++ -Ox
• A fordító buta és mindig a legrosszabbra készül
– __restrict__ pointerek
• Biztosítja, hogy csak az a pointer és annak másolata éri el a megcímzett területet
• Még nem C++ szabvány, de C99 és szinte mindenki támogatja
26
Auto-vektorizáció
• A fordító nem tud semmit az alignmentről se
– gcc: __attribute__(aligned(16))
– Visual C++: __declspec(align(16))
• Csak 4,8,16 elemű for ciklusokat vektorizál
– Ami pont egy utasítás
• Nem használja ki a maszkolás lehetőségét se
• A gcc buta, a Visual C++ még butább
– Megoldás: Intrinsic-ek
27
Intrinsic
• Függvényeknek álcázott assembly utasítások – Nem a legolvashatóbb kódot kapjuk vele
• A fordító foglalkozik a regiszterekkel – Mi használhatunk változóneveket
• Egy intrinsic függvény garantáltan egy utasítássá fordul
• Be kell kapcsolni a megfelelő architektúra utasításait – gcc –m<architektúra> (pl.: -msse2) – Visual C++: C/C++ / Code Generation / Enable
Enhanced Instruction Set / <architektúra>
28
Intrinsic MMX
• MMX
– MM0-MM7 64bites regiszterek
• Az FPU regiszterinek az alsó 64bitje
– Egyszerű SIMD utasítások egész számokhoz
• 2x 32 bit, 4x 16bit vagy 8x 8bit
– Pentium with MMX és Pentium II óta minden támogatja
• (ezek 2000 előtti processzorok)
– Nem igazán használjuk már, van jobb
29
Intrinsic SSE
• XMM0-XMM7 128bites regiszterek
– 64biten további 8 regiszter: XMM8-XMM15
• Floating point SIMD utasítások
– 4x 32bit float műveletek
• Gyorsabb a memória elérés ha 16 bájtos határra vannak igazítva a címek
• Pentium III óta minden támogatja (1999)
• (Oprendszer támogatás kell hozzá)
30
Intrinsic SSE2
• Innentől kezd igazán hatékony eszköz lenni
• Az XMM regiszterekben több típus használható
– 2x 64 bit double
– 4x 32 bit float
– 4x 32 bit int, 8x 16bit short, 16x 8bit char
• MMX utasítások az XMM regiszterekre
• Pontatlanabb, mint az FPU (64bit vs 80bit)
31
Intrinsic SSE3
• Horizontális műveletek az XMM regisztereken
– Vertikális amikor összeadok két regiszterben minden mezőt párhuzamosan
– Horizontális amikor egy regiszterben összegzem az elemeket
• Pentium 4 NetBurst mikroarchitektúra óta (2000)
32
Intrinsic SSSE3, SSE4.1, SSE4.2
• További horizontális utasítások az MM és az XMM regiszterekhez
• Néhány további keverés utasítás
• Bájtonkénti keverés
• Minimum, maximum utasítások
• Egyéb mindenféle spéci utasítások kihegyezve egy egy konkrét feladatra
• Core mikroarchitektúra óta SSSE3 (2006), 2008-as Core i7 óta SSE4
33
Intrinsic AVX
• Új 256 bites YMM regiszterek – Alsó 128 bitben az XMM regiszterek
• SSE utasítások kiterjesztve 256 bitesre • 4x 64bit double, 8x 32bit float • Nem destruktív SSE utasítások
– Eddig: a = a+b – Mostantól: c = a+b
• A 128 bites XMM regiszterekre is
• Ez már 8x gyorsítás float számításoknál! • Sandy Bridge óta (2011) elérhető
34
Intrinsic AVX2, AVX-512
• Haswell óta AVX2, még nem túl sok gép támogatja...
• Integer utasítások az YMM regiszterekre
• Néhány további floating point utasítás – FMA – FFT-re, skalár szorzatra optimalizálva
• AVX-512 a legújabb Xeonokban nemsokára – ZMM regiszterek 512bitesek, alsó 256 az YMM
– Kiterjesztve az AVX, AVX2 utasítások
– További 2x-es gyorsítás
35
Intrinsic
• Intrin.h – Itt vannak a C++ deklarációk
– __m256 típus az YMM regiszterekhez
– ARM architektúrán az arm_neon.h-ban vannak ARM-os SIMD utasítások
__m256 a,b,c; a = _mm256_set_ps(1,2,3,4,5,6,7,8); b = _mm256_set_ps(8,7,6,5,4,3,2,1); c = _mm256_add_ps(a,b); //c = [9 9 9 9 9 9 9 9];
36
Intrinsic + OpenMP
• Az OpenMP több magot fog be munkára
• A vektorizáció egy processzormagon párhuzamosítja a feldolgozást
• A gyorsulás mértéke a kettő együttes használatával a kettő szorzata
• Core i7 esetén (2x4 mag HT-vel) az egyszálas programhoz képest floatokkal számolva akár 64-szeres gyorsulás! (Ideális esetben)
37
GLSL/HLSL
• OpenGL 2.0-tól GLSL shader nyelv • DirectX HLSL • Hasonlít a két nyelv • Létrehozhatunk framebuffereket (nem csak a
képernyőn) és tudunk bele renderelni • Vertex Shader nem csinál semmit csak átnyomja magán
a pontokat • Fragment Shader pixelenként megy, ebben tudunk
érdemi feladatokat megcsinálni • Sok beépített függvény, egyszerűbb inicializáció, elég
jól hordozható
39
GPGPU
• OpenCL és CUDA nagyjából ugyan azt tudja
• OpenCL nem csak GPU-kat támogat és nem csak Nvidia eszközökön használható
• Az Nvidia ott tesz keresztbe az OpenCL-nek ahol tud
– Végre támogatják az OpenCL 1.2-őt (2.1-nél járunk)
• A CUDÁnak jobb a szoftvertámogatottsága
40
GPGPU: CUDA, OpenCL
• Közös vonások
• Adatok fel-/letöltése
– PCIExpress busz overheadje
• Kernel hívás
– Viszonylag nagy overhead
• Kernelhíváson belül nincs globális szinkronizáció!
– Új kernel hívásával pedig nagyon költséges
41
GPGPU: OpenCL, CUDA
• CUDA terminológia szerint egy kernelhívásnál van egy Grid
• A Grid tartalmaz blokkokat
• A blokkok futtatnak szálakat
• Van globális és lokális id minden szálhoz
– Globális a Grid-en belüli hely
– Lokális a blokkon belüli hely
42
GPGPU Memória
• Globális memória – A kártyára integrált GDDR5 memória
– Sok (2-8Gb)
• Lokális memória (Shared memory) – A GPU belsejében a multiprocesszorhoz tartozó
memória
– Egy blokk ezt látja
• Privát memória – Egy szál látja (regiszterek kb.)
43
GPGPU Memória
• Konstans memória
– Mint a globális memória, de úgy van cachelve, hogy minden szál könnyen tudja párhuzamosan olvasni
• Textúra memória
– Mint a globális memória, de a textrázó egységek tudnak benne interpolálni és ehhez is van külön cache
44
GPGPU Memória
• Privát memória
– 1 órajel ciklus
• Lokális memória
– 10 órajel ciklus
• Globális memória
– 100 órajel ciklus
• Konstans és textúra memória
– 100 órajel ciklus amíg nincs cachelve, utána 10
45
GPGPU Utasítások
• Gyakorlatilag nincs ugró utasítás – Az if mindkét ága lefut, de maszkolni lehet, hogy
melyik threadekben hajtódjon ténylegesen végre – Nem jó a nagy elágazási tényező
• Fmax, fmin használata sokszor segít • Nem hatékony ha egy blokkon belül a szála különböző ágára
futnak az if-nek
• Nem túl hatékonyak a nagy for ciklusok – Fejtsük ki az összes ciklusát egy for-nak (ha rövid)
• #pragma unroll
• A double 16-szor lassabban számolódik, mint a float
46
GPGPU Memória elérés
• Amit többször is használunk töltsünk be a lokális memóriába
– Ha csak az az egy szál használja, akkor a privát memóriába
• A ténylegesen párhuzamosan futó szálak számát befolyásolja a privát memória használata
– Kb. 1kb ami egy szálnak jut ideális esetben
47
GPGPU Memória elérés
• A memóriából csak sorban lehet olvasni és csak 256 bájtos határra igazított blokkokat – Ha nincs igazítva az adat és átcsúszunk akkor két
olvasás történik
– Ha nem sorban olvasunk akkor több olvasás történik • Egyre több mintát támogat a memória vezérő, nem csak a
tökéletesen rendezett sort
• A memória egy bankját egyszerre egy blokk érheti el, ha több blokk hivatkozik rá, akkor meg kell várniuk egymást – Konstans memória használata olvasáskor segít
48
GPGPU Kernelhívás
• A Grid méret mindig osztható kell, hogy legyen a blokk mérettel
• Érdemes egyetlen kernelhívást csinálni és mindent abban kiszámolni
• Az adatok fel-/letöltése miatt csak nagy problémák esetén érdemes GPU-t használni
• Minél több szálat indítunk annál jobb a GPU kihasználtsága – 1’000’000 szál még nem olyan sok, hogy tökéletesen
kihasználja a GPU-t
49
GPGPU Cache
• A textúra úgy van cachelve, hogy 2D-ben a közeli pontok is betöltődjenek egy olvasáskor
• CUDA esetén a shared memory és a cache közös tárolóban van
– Ugyan olyan gyorsak
– Érdemes előre betölteni amit használni fogunk, amennyiben többször is olvassuk
50
GPGPU textúra memória
• Nem mindig írható
• Textúrák speciálisan cachelve – 2D-ben Szomszédos pixelek töltődjenek be, ne
sorfolytonosan • Csigavonl minta a tároláshoz
• Lineáris interpoláció hardware támogatása – Ugyan olyan gyors, mint egy pixel kiolvasása
– Fel lehet használni biköbös interpolációhoz is • 4 olvasás a 16 helyett
51
Lokális szinkronizáció
• Egy blokkon belül bevárhatják egymást a szálak
– Hasznos például a lokális memória feltöltése után
– Lokális memóriában elvégezhetnek redukciós feladatokat
– Bármilyen más blokkon belüli kommunikáció
52
OpenCL
• Macerásabb inicializálás
– Cserébe nem csak GPU-kat támogat
• Platform kiválasztása
• Eszköz kiválasztása a platformon
• Kontextus létrehozása
• Commad queue létrehozása
• Egy GPU esetén így kész vagyunk, több GPU esetén többet kell ezekből létrehozni
53
OpenCL kernelhívás
• Kernel külön .cl fájlban forrásként mellékelve – Buildelni kell a program indulásakor -> program objektum
• Lassú
– Minden kernelből kernel objektumot kell csinálni
• Minden kernel híváshoz be kell állítani minden paramétert egy-egy függvényhívással
• Még egy függvény hívás elindítja (akár aszinkron módon) a kernelt – AMD aszinkron, NVidia szinkron – Itt kell megadni a globális méretet 1,2 vagy 3 dimenzióban – A lokális méret opcionális, rábízhatjuk a driverre
• Nem túl beszédesek a hibaüzenetek
54
OpenCL textúrák
• 1D, 2D, 3D, 2D textúra tömbök – Textúratömbök OpenCL 1.2 óta
• 1D, 2D, 2D textúra tömbök írhatóak
• 3D csak kiegészítéssel írható GPU oldalról – Tömb írható és a tömböt bele lehet másolni
• Van Nearest Neighbour, Lineáris interpoláció – Hardver támogatás, a lineáris interpoláció ugyan
olyan gyorsan olvas!
– Textúrázó egységek számától függ a teljesítmény
55
OpenCL kernelek
__kernel void immul(float q,
__write_only image2d_t dest,
__read_only image2d_t src
)
{
sampler_t pixelsampler =
CLK_NORMALIZED_COORDS_FALSE|
CLK_ADDRESS_CLAMP_TO_EDGE|
CLK_FILTER_NEAREST;
int2 id = (int2)(get_global_id(0),get_global_id(1));
float tmp = read_imagef(src,pixelsampler,id).x*q;
write_imagef(dest,id,(float4)(tmp,0,0,0));
}
56
OpenCL típusok
• C99 szabvány szerinti skalárok • Vektor típusok
– Int2, int4 – float2, float4
• Van mindennek CPU-s megfelelője – cl_float2 mezőit az s tömbön keresztül érjük el cl_float2 a; a.s[0] = 0; a.s[1] = 42;
• A vektor változók használata csak az olvashatóságot javítja, nem gyorsítanak
• A vektorváltozóknál az operátorok elemenként hajtódnak végre
57
OpenCL függvények
• Szintén C99 • Matematikai
– fabs, fmin, fmax, sin, cos, tan,... – Vektor műveletek
• dot, cross,...
• Id és méret lekérdezés • Textúra kezelés
• OpenCLUtils.h és .cpp-t írtam már, printf szerű
kernel hívások, inicializáció – Csak egy GPU kezelése – Felrakjuk a tárgyhonlapra
58
CUDA
• Egyszerűbb inicializáció
– Csak NVidia GPU-val megy
• Ha egy GPU van a gépben már tölthetjük is fel az adatokat és hívhatjuk a kernelt
• .cu fájlok
– Az nvcc szedi szét C++-ra és CUDA C-re
– A C++ tovább megy a fordítónak
– A CUDA C-ből ptx kódot csinál az nvcc
59
CUDA
• Elég a ptx fájlokat adni a projekthez
– Lehet forrást is adni
• A GPU-ra írt kód keveredik a CPU-s kóddal, spéci függvények a kernelek.
60
CUDA Kernelhívás
• Olyan, mint egy template függvényhívás kernel_ipm_extend <<< gridsize, blocksize >>> (
gDst_ipm,
dst_y,
dst_x,
dst_y_memstride
);
61
CUDA textúrák, kernelek, típusok
• Mint OpenCL-nél
• De itt mindent lehet írni
• Template-re hasonlító szintaxis itt is
• Interpoláció itt is hasonlóan
• Itt a kernel egy __global__ függvény
• A méret és az id kitüntetett változókban
• Hasonló típusok itt is, ugyan úgy csak az olvashatóságot javítja a vektorizáció
62
CUDA függvények
• Hasonló beépített függvények
• Sok jó könyvtár van hozzá
– CuFFT
– CuBLAS
• Az OpenCL-hez is van FFT, BLAS, meg pár könyvtár
– A dokumentáció ott rosszabb, és körülményesebb használni
63
GPGPU Esettanulmányok
• Szűrés – Naiv – Lokális memória használata
• Flood fill – Naiv – Lokális memória használata – Ping-pongozás
• Parallel reduction – Szummázás
• Hisztogram számítás
64
GPGPU Naiv szűrés
• Minden pixel párhuzamosan számolódik • Minden pixel beolvassa a számára szükséges
bemeneteket • For ciklusban konvolúció
– Esetleg egyéb művelet nem konvolúciós szűrőnél
• Eredmény kiírása
• Jelentősen többször olvaszuk be a globális memóriából a bemeneti kép pixeleit, mint amennyiszer szükséges
65
GPGPU Szűrés
• Rakjuk a lokális memóriába a pixeleket • A for ciklus futhat a lokális memórián • A blokk szélén több pixelt kell beolvasni, hogy ne
lógjunk ki – Az if használata nem jó, és ilyenkor se szeretnénk a
globális memóriából olvasni
• Az első lépés a pixelek beolvasása lokális memóriába – Ez ráadásul két fázisban történik, a kilógó pixelek miatt
• Utána szinkronizáció kell • Végül lehet szűrni
66
GPGPU Szűrés
• Szeparábilis szűrők
– A szűrő előáll egy horizontális és egy vertikális szűrő szorzataként
– SVD-vel lehet meghatározni
• Előfordulhat, hogy több szeparábilis szűrő összegeként áll elő
– Nem csak egy szinguláris érték van
– Ilyenkor nagyság szerint az első néhány értékkel legkisebb négyzetes hiba értelemben lehet legjobb közelítést adni
– A szűréseket végezzük két kernel hívással (vertikális és horizontális) és összegezzünk egy harmadik kernellel
– Gauss, Box szűrő ilyen
67
GPGPU Szűrés
• 30-as kernelméretnél már nem annyira gyors
• Szűrjünk frekvenciatérben!
• FFT is gyorsabb GPU-n
• FFT + Szorzás + iFFT még mindig megéri
– Érdemes az AMD vagy NVidia által adott csomagokat használni
69
GPGPU Naiv Flood fill
• Paintes vödör
• Lehet tartományt is megadni neki, nem kell teljesen azonos pixelekre folyatni
– Watershed innen már nem sok módosítás
• Minden pixlen megvizsgáljuk, hogy ahhoz a színhez tartozik-e amit el akarunk árasztani
• Ha igen, akkor megnézzük, hogy van-e elárasztott szomszédja és ha van akkor őt is megjelöljük
• Ezt addig ismételjük, amíg van változás a képen
70
GPGPU Flood Fill
• Nagyon kevés pixel változik egy lépésben – Rengeteg globális szinkronizáció
• Töltsük be a lokális memóriába a pixeleket – Meg még egy sort a szomszéd blokkokból
• Végezzük el ezen a területen lokális szinkronizációkkal a flood fill-t
• Írjuk ki a globális memóriába a saját területet • Ezt ismételgessük
– Kb. 200-szor gyorsabb, mint az előző megoldás
• Vannak szofisztikáltabb megoldások a lokális memóriában végzett műveletekre 71
GPGPU Ping-pongozás
• Iteratív algoritmusoknál használjuk
– A Flood fill is iteratív algoritmus
• Érdemes két puffert használni
• Először az egyikből a másikba dolgozunk
• Utána a másikból az egyikba dolgozunk
• Nem kell mindig új puffert foglalni
• Ha nem 3D textúra, akkor tudunk írható és olvasható puffereket csinálni
72
GPGPU Naiv Parallel reduction
• Cél az összes pixel intenzitásának összegét kiszámolni
• Adjuk össze párosával a pixeleket és akkor keletkezik egy fele akkora kép amin megismételhetjük a műveletet
• Sok globális szinkronizáció
73
GPGPU Parallel reduction
• Összegezhetünk a lokális memóriában régiókat
• 256-os blokkmérettel 8-ad annyi globális szinkronizáció
• Érdemes folytonos blokkokat olvasni a memóriából, az összefésülést kevésbé szereti
– Figyeljünk a hatékony indexelésre a globális memória esetén
• Lehet minimumot, maximumot is sok egyebet is számolni ami asszociatív és kommutatív művelet
74
Hisztogram
• Minden pixelre megvizsgálom, hogy melyik oszlophoz tartozik
• Az oszlopot növelem egyel
• Atomi műveletek kellenek! – NVidia Maxwell GPU-k óta (900-as sorozat) van a
lokális memóriába is hardware-es atomi művelet
• Érdemes a lokális memóriába összegezni képrészleteket és a végén összeadni a kis hisztogramokat
75
Gyorsító kártya technológiák
• OpenCL • OpenMP • Intel fordítója a Xeon Phi kártyákhoz, fejlesztés
C/C++ nyelven • FPGA
– Verilog/Vhdl – CPU oldalon driver
• Intel+Altera FPGA-k a Xeon sorozatban remélhetőleg hamarosan – Az egyik mag helyett FPGA a QPI buszon
76
Xeon Phi
• Eredetileg a GPU-k ellen indult – Nem vált be
• Nagyon drága – Tesla kártyákkal versenyben
• Dinamikus párhuzamosság sokkal jobb támogatása
• Hagyományos programozási módszerek kihasználhatósága – OpenMP
• OpenCL-el is programozható – Nem támogatja a textúrázást 77
Xeon Phi – Knights Corner
• 16Gb memória
• 50 darab P54C mag (Pentium 1995-ből)
• Kiegészítve AVX feldolgozással
• 50x 8 float művelet = 400 művelet egyszerre
– Sokkal rugalmasabb, mint a GPU
• Egyszerre többféle művelet
78
Xeon Phi – Knights Landing
• Berakható a fő processzor helyére • 72 Airmont Atom mag
– 4 szál magonként (HT) – 2 Vektor processzor magonként
• AVX-512 utasítások • TSX
– Hardware támogatás tranzakcionális memóriához
• 8-16Gb közeli 3D MCDRAM memória • 384Gb távoli DDR4 memória • 2304 float párhuzamosan
– Közben ugyan ennyi nem vektorprocesszoros művelet végrehajtása 79
Egyéb optimalizációs tanácsok
• Kerüljük az osztást
– Na meg a gyökvonást, nem egész kitevős hatványozást, logaritmust, trigonometriát...
– Ha el tudod tárolni a reciprokát és szorozni, akkor úgy csináld
• Kerüljük az int, float, double konverziókat
• A float gyorsabb, mint a double
– A char meg még gyorsabb
80
Egyéb optimalizációs tanácsok
• Bonyolult műveleteknél megérheti táblázatból kiolvasni az eredményt számolás helyett
• A fordítónál kapcsolj be mindent releasenél – gcc: -O3
– Visual C++: -Ox
• Várakozó ciklusba mindig rakjunk valami sleep utasítást – Különben nagyon pörgeti a processzort és nem
tud mást csinálni
81
Egyéb optimalizációs tanácsok
• Használjuk az inline függvényeket – A fordítót is be lehet állítani, hogy automatikusan
inlineoljon
• Használjunk a __cdecl hívásó konvenció helyett hatékonyabbat ahol lehet – __fastcall, __vectorcall
• Sebesség szempontjából kritikus helyeken kerüljük a fölösleges objektumokat – Így nem kell konstruktort és destruktort hívni
• Használjunk jól optimalizált könyvtárakat – FFTW, Intel MKL,...
82