CUDA C/C++ programozás

Slides:



Advertisements
Hasonló előadás
A számítógép felépítése
Advertisements

CUDA.
LEgyméretű tömbök, vektorok Alkalmazott Informatikai Tanszék SZÁMÍTÁSTECHNIKA I. dr.Dudás László 17./0. lVektorok definiálása lVektorok definiálása kezdőértékekkel.
V 1.0 Szabó Zsolt, Óbudai Egyetem, Haladó Programozás Parallel.For()
1 Hernyák Zoltán Programozási Nyelvek II. Eszterházy Károly Főiskola Számítástudományi tsz.
 Adat- és feladat párhuzamos modell  Az ISO C99 szabvány részhalmaza  párhuzamos kiegészítésekkel  Numerikus műveletek az IEEE754 alapján  Beágyazott.
 CUDA mint architektúra  Párhuzamos feldolgozásra optimalizált architektúra  CUDA mint GPGPU keretrendszer  Runtime és Driver API  CUDA C/C++  NVCC.
© Kozsik Tamás Beágyazott osztályok A blokkstrukturáltság támogatása –Eddig: egymásba ágyazható blokk utasítások Osztálydefiníciók is egymásba.
Szoftevrismeret Operációs rendszerek.
A számítógép felépítése
7. előadás (2005. április 12.) Láncolt lista File kezelés 1.
4. előadás (2005. március 8.) Pointerek Pointer aritmetika
UNIVERSITY OF SZEGED D epartment of Software Engineering UNIVERSITAS SCIENTIARUM SZEGEDIENSIS Programozás II. 9. Gyakorlat Alap file műveletek.
UNIVERSITY OF SZEGED D epartment of Software Engineering UNIVERSITAS SCIENTIARUM SZEGEDIENSIS Programozás II. 6. Gyakorlat const, static, dinamikus 2D.
Optimalizálás nemklasszikus architektúrákon
Egy feladat megoldásának gondolatmenete. Feladat: Generáljuk véletlen számokat 1 és 6 között, amíg legalább 10 darab egyes és 10 darab hatos nem lesz.
Programozás I. Horváth Ernő.
LDinamikus tömbök, kétdimenziós tömbök Alkalmazott Informatikai Tanszék MŰSZAKI INFORMATIKA dr.Dudás László 21./0. lVektorok létrehozása futásidőben, dinamikusan.
PIC processzor és környezete
CISC - RISC processzor jellemzők
C++ Alapok, első óra Elemi típusok Vezérlési szerkezetek
A memória.
Készítette: Bodor Béla Tanár: Szabó Dániel Iskola: Egressy Gábor Kéttannyelvű Műszaki Szakközépiskola Iskola címe: 1149 Budapest, Egressy út 71. MEMÓRIÁK.
A memória tárolja a végrehajtandó programokat és a feldolgozásra váró adatokat. A számítógép memóriája adattárokból áll. Minden ilyen adattár memóriaelemekből.
PHP I. Alapok. Mi a PHP? PHP Hypertext Preprocessor Szkriptnyelv –Egyszerű, gyors fejlesztés –Nincs fordítás (csak értelmező) Alkalmazási lehetőségek:
Programozás Az adatokról C# -ban.
Tömbök Csernoch Mária.
A számítógép teljesítménye
Nikházy László Ureczky Bálint Konzulens: dr. Horváth Gábor
Mikrokontroller (MCU, mikroC)
Tömbök és programozási tételek
1.4. Fordítás, szerkesztés, az objektumkönyvtár használata.
Programozási nyelvek.
Hernyák Zoltán Programozási Nyelvek II.
1 Hernyák Zoltán Programozási Nyelvek II. Eszterházy Károly Főiskola Számítástudományi tsz.
Hernyák Zoltán Programozási Nyelvek II.
Processzor, alaplap, memória
Programozási tételek.
Objektum orientált programozás
CUDA C/C++ programozás Textúra memória A segédanyag készítése a TÁMOP A/ Nemzeti Kiválóság Program című kiemelt projekt keretében.
Objektum orientált programozás
Programozás I. Típus algoritmusok
CUDA C/C++ programozás Szál struktúra A segédanyag készítése a TÁMOP A/ Nemzeti Kiválóság Program című kiemelt projekt keretében.
CUDA C/C++ programozás Egyéb eszköztárak vegyesen A segédanyag készítése a TÁMOP A/ Nemzeti Kiválóság Program című kiemelt projekt.
Párhuzamosság, kritikus szakasz, szemaforok Operációs rendszerek.
CUDA C/C++ programozás GPU tulajdonságainak lekérdezése A segédanyag készítése a TÁMOP A/ Nemzeti Kiválóság Program című kiemelt.
A Visual Basic nyelvi elemei
CUDA C/C++ programozás Atomikus műveletek A segédanyag készítése a TÁMOP A/ Nemzeti Kiválóság Program című kiemelt projekt keretében.
Ismerkedjünk tovább a számítógéppel
Függvények a C nyelvben 1 Függvényeket a következő esetekben szokás írni: Ha ugyanazt a tevékenységet többször is el kell végeznünk ugyanolyan típusú,
C Programozási alapok.
CUDA C/C++ programozás CUDA C bevezetés A segédanyag készítése a TÁMOP A/ Nemzeti Kiválóság Program című kiemelt projekt keretében.
Írja fel a tizes számrendszerbeli
Memóriakezelés feladatok Feladat: 12 bites címtartomány. 0 ~ 2047 legyen mindig.
Excel programozás (makró)
1 Számítógépek felépítése 5. előadás a CPU gyorsítása, pipeline, cache Dr. Istenes Zoltán ELTE-TTK.
Függvények, mutatók Csernoch Mária. Függvények függvény definíciója az értelmezési tartomány tetszőleges eleméhez hozzárendel egy értéket –függvény helyettesítési.
Informatikai gyakorlatok 11. évfolyam
Programozás III ÖTLETEK A FELADATMEGOLDÁSHOZ. A HF-EK APROPÓJÁN Néhány javaslat: 1. Jó lenne, ha a feladatmegoldás előtt átnéznék az előadás-anyagokat.
Krizsán Zoltán, iit C# osztályok 2 Adattagok  Osztály hatáskörben definiált változó.  Formája: [attribútum] [módosító] típus azonosító [=kezdő érték][,
1 A számítógépek tárolói. 2 Memória Memóriaszó  A tárak olyan egységei, melyek egyetlen művelettel kezelhetők.  A legrövidebb memóriaszó a byte (bájt)
Szécsi László 3D Grafikus Rendszerek 15. előadás
GPGPU – CUDA 1..
Excel programozás (makró)
Lineáris egyenletrendszerek megoldása
GPGPU – CUDA 2..
Változók.
Nikházy László Ureczky Bálint Konzulens: dr. Horváth Gábor
Informatikai gyakorlatok 11. évfolyam
Cache példák 2019 (IMSC).
Előadás másolata:

CUDA C/C++ programozás Közös és konstans memória A segédanyag készítése a TÁMOP 4.2.4.A/2-11-1-2012-0001 Nemzeti Kiválóság Program című kiemelt projekt keretében zajlott. A projekt az Európai Unió támogatásával, az Európai Szociális Alap társfinanszírozásával valósul meg.

GPU memória fajták Valójában a CUDA architektúra szerint a CUDA magok többféle adattárolóhoz férnek hozzá. Regiszterek Írható-olvasható Közös memória Irható-olvasható Konstans memória Csak olvasható Textúra memória Grafikus memória

Regiszterek Gyors elérés. A változók számára fenntartott hely. Minden szál külön készletet kap belőlük. 32 bites tárolóegységek. Korlátozott mennyiségben állnak rendelkezése. Minden változó, illetve programba tett elágazás regiszter-t használ. Ha egy blokkban a szálak együttesen több regisztert szeretnének használni, mint amennyi a multiprocesszorban van, akkor a kernel függvény nem indul el. Felső korlát a blokkonként indítható szálak számára. Nem árt „takarékoskodni” a változókkal.

Közös memória A GPU multiprocesszorában található memória. Az indított blokkban minden szál hozzáfér. A szálak adatokat adhatnak át rajta keresztül egymásnak. Számítási képességtől függően: Mérete 16, vagy 48 Kbyte lehet. 16, vagy 32 bank-ba szervezve érhető el. 32 bites szavakban az egymást követő szavak, egymást követő bankban kapnak helyet. Minden memóriabanknál egyszerre 2 órajelenként egy 32 bites elem olvasása vagy írása lehetséges. Ha egy bank különböző elemeit akarja több szál olvasni egyszerre az „bank konfliktus”-t okoz. (a kérések szekvenciálisan elégítődnek ki, 2 órajelenként)

Memóriabankok elérése a, nincs konfliktus Egymást követő szálak egymást követő bankokat érnek el. b, nincs konfliktus Szálak és bankok véletlen permutációja c, nincs konfliktus Van bank amit több szál is elér de ugyanazt az elemet olvassák d, nincs konfliktus Sok szál 2 bankot olvas. De csak egy elemet. a) b) c) d)

Közös memória használata Deklarálás: __shared__ előtaggal, a kernelben! A kernelhez tartozik, akkor foglalódik, amikor a blokk létrejön, és a blokkal együtt megszűnik A foglalt méretet memóriaterület méretét a deklaráláskor meg kell adni! (fordítási időben tudni kell) Általában a blokkmérethez kötött. (lehet többdimenziós is) #define BLOCK_DIM 16 ... __global__ void kernel(...) { __shared__ float cache[BLOCK_DIM][BLOCK_DIM]; }

Közös memória használata Mint az általános változóknál. Figyelni kell a szinkronizációra. __global__ void kernel(...) { __shared__ float cache[BLOCK_DIM]; ... cache[threadIdx.x] = a + b; ... value = cache[BLOCK_DIM-threadIdx.x-1]; }

Szálak közötti szinkronizáció A szálak közötti kommunikáció problémákat vet fel. A szálak egymás által előállított korábbi adatokat olvasnak a közös memóriából. A szálak feldolgozása nem teljesen párhuzamos. Előfordulhat, hogy egy szála futásában előrébb tart mint egy másik. Ha egy szál olyan adatot próbál olvasni amit a másik még nem írt be a közös memóriába, akkor a számítás kiszámíthatatlanná válik. cache[threadIdx.x] = expf(threadIdx.x); ... value = cache[BLOCK_DIM - threadIdx.x];

Szálak közötti szinkronizáció A szinkronizációtegoldó függvény: __syncthreads(); A blokk szálainak a futását megállítja, amíg a blokk minden szála meg nem hívta a függvényt. Segítségével egy ponton szinkronizálható a szálak futása. cache[threadIdx.x] = expf(threadIdx.x); __syncthreads(); ... value = cache[BLOCK_DIM - threadIdx.x -1];

Gondok a szinkronizációval A szinkronizáció lassíthatja a kódot. Szabadságot vesz el a szálütemezőtől. A multiprocesszor üresjáratban állhat, amíg néhány szál adatra várakozik. Nem körültekintő használat mellett megakaszthatja a programot. Főleg elágazásban problémás. Ha a blokkban van szál ami nem hívja meg, akkor a többi szál a végtelenségig várakozik. if(blockIdx.x % 2) { ... __syncthreads(); } // Bukta van. :( A szálak egy része be sem jön az // if-be. A többi a végtelenségig vár ezekre // a szálakra, hogy szinkronizáljanak.

Példa a közös memória használatára A közös memória használható például a blokkon belül szálak eredményeinek összegzésére. // Szál eredménye a közös memóriába cache[threadIdx.x] = result; __syncthreads(); // összegzés logaritmikus ciklussal (tömb felezés) int i = blockDim.x/2; while (i != 0) { if (cacheIndex < i) cache[cacheIndex] += cache[cacheIndex + i]; i /= 2; } // eredmény a cache[0]-ban.

Skaláris szorzat példa Vektorok skaláris szorzata. 𝑢 ∙ 𝑣 = 𝑖=1 𝑛 𝑢 𝑖 ∗ 𝑣 𝑖 Ötlet: Indítsunk 32 blokkot egyenként 256 szállal. A vektorokat osszuk fel 32*256 részre. Minden szál kiszámolja az összeget a rá kiosztott indexekre. A blokkokon belül összegezzük a 256 szál eredményeit. Végül a 32 blokkban, előáll 32 összeg. Azt kiírjuk a grafikus memóriába, majd továbbítjuk a CPU-hoz a végső összegzésre. 10_DotProduct.cu

Egy másik példa 11_OutOfSync Példa ami bemutatja, hogy miért kell szinkronizálni. A kernelben (a példa 27. sorában van egy __syncthreads() függvényhívás. Próbáljuk, ki, hogy mi az eredmény azzal, vagy a nélkül.)

Konstans memória Korlátozott méretű csak olvasható memóriaterület. Max 64 Kbyte A grafikus memóriában foglalódik le. Gyorsítótárazva lesz. Az elem az első olvasása után egy L1 szintű gyors elérésű gyorsító tárba kerül. Ha több szál olvassa ugyanazt az adatot, akkor nagy- mértékben gyorsít az elérésen. Az egymás után történt olvasásokat is gyorsítja. Viszont korlátozott területű és nem írható.

Konstans memória használata Deklarálás: __constant__ előtaggal, a globális scope-ban. A foglalt méretet a deklaráláskor meg kell adni! (fordítási időben tudni kell) Feltöltése: A „CudaMemcpyToSymbol” függvénnyel a CPU kódban. CudaMemcpyToSymbol(cél, forrás, byte_szám) Elérése a kernelben. Mintha globális memória lenne. Csak nem lehet írni.

Példa nagy vonalakban __constant__ float const_mem[256]; __global__ void kernel(...) { ... for(i=0; i<256; i++) { value = const_mem[i]; } int main() { float const_temp[256] = ...; cudaMemcpyToSymbol(const_mem, const_temp, 256 * sizeof(float)); return;

Példa részletesen 12_Ray_Const.cu Egyszerű sugárkövetés a GPU-n.

Extra tudnivaló a konstans memóriával kapcsolatban A konstans memória olvasásánál a memóriakezelő minden fél warp-nak képes továbbítani egy konstans memóriából olvasott adatot. De mi az a fél warp?

Warp-ok Az indított blokkokban a szálakra vonatkozik még egy csoportosítás, ami „warp”-ba kötegeli a szálakat. Az indításnál minden szál kap egy egyedi azonosítót. (thread ID) ID = threadIdx.x + threadIdx.y * blockDim.x + threadIdx.z * blockDim.x * blockDim.y; A warp-ok 32-es csoportokba kötik az egymás után következő indexekkel rendelkező szálakat. 1. warp: 0, …, 31; 2. warp: 31, …, 63; Stb. A fél warp egy warp első vagy második fele.

Warp-ok tulajdonságai Az egyazon warp-ba tartozó szálakra vonatkozik pár tulajdonság. Egy warp szálai egyszerre ugyanazt az utasítást hajtják végre. Tehát szinkronizáció nélkül sem halad át a warp egy utasításon amíg az összes szála végre nem hajtotta. Ez annyira szigorú, hogy elágazás esetén is a warp összes szála végrehajtja az utasításokat. Csak azok a szálak mikre az elágazás feltétele nem teljesül eldobják az eredményt. if(threadIdx.x==0) { ... } // egy szál számol, és mellette a többi 31 // „türelmesen kivárja”

Warp-ok és a konstans memória A fél warp-oknak fontos szerepe van a memóriavezérlés szervezésében. Általában közös memóriakezelő csatornákat kapnak. (A grafikus memóriánál majd látjuk hogy mit jelent ez.) Konstans memória olvasásakor a fél warp szálai között minden esetben egyszerre egyetlen olvasott adat lesz szétszórva. Ha minden szál ugyanazt az adatot kérte, akkor gyorsan megkapják. Ha különböző adatokat kértek, akkor a kérések szekvenciába rendeződnek, és egymás után lesznek kielégítve. Ez meg párhuzamosan a két fél warp-on, de amíg mind a két fél összes szála meg nem kapta a kért adatot, addig a warp szálai állnak. Lassabb lehet, mint ha a globális memóriából olvasnánk.

És ha már a warp-oknál tartunk… A GPU-ban a memóriakezelő a memóriát részegységekben kezeli, és blokkosan olvassa/írja. A memóriakezelő 32, 64 vagy 128 Byte méretű adatblokkot tud elérni, csakis 32-vel osztható kezdőcímtől indulva. Az elért blokkban utána kiválasztódik, hogy pontosan mely Byte-okat kell kiolvasni/beírni. A GPU számítási képességétől függően különböző módokon lehet hatékonyan kezelni a memória elérést.

Memórialérés 1.0-s, és 1.1-es számítási képességű GPU-nál: Ideális esetben: Pl. 32 bites szavak igazított olvasás kor, ha: A fél warp egymás után következő szálai egymás után következő szavakat olvasnak a memóriából. És az első elért memóriaszó címe 32-vel osztható. Akkor a memóriában egy darab 64 bites olvasás van, és a warp minden szála megkapja a kért adatot. Különben (probléma): A fél warp memóriaelérései 16 darab különálló 32 bites memóriaelérésre lesznek visszavezetve. (16-szor annyi munka, és rengeteg idő)

Memórialérés 1.2-es, és 1.3-as számítási képességű GPU-nál: A helyzet sokkal jobb. A memória elérés 32, vagy 64 Byte-os blokkokban is történhet, és A GPU memóriakezelője megállapítja, hogy a memória mely blokjaihoz próbálnak hozzáférni a szálak. És elosztja a memória hozzáféréseket, hogy minden blokkhoz legfeljebb 1-szer kelljen hozzáférni. 2.x, 3.x-es számítási képességnél: A kezelt memóriaegység megint 32 Byte-os. De van gyorsítótár, amivel meg lehet gyorstani az olvasást.

Memória olvasás illusztrálva 32-vel osztható címhez igazítva szekvenciálisan

Memória olvasás illusztrálva 32-vel osztható címhez igazítva, de nem szekvenciálisan

Memória olvasás illusztrálva Nem 32-vel osztható címhez igazítva, de szekvenciálisan