Cuda - alapjai példák

Ugyanakkor tervezők __host__ __device__ és együtt is használható (ami azt jelenti, hogy a megfelelő funkció végezhető el a GPU és a CPU - a megfelelő kódot automatikusan a fordító által generált mindkét platformon). Előírást __global__ __host__ és nem lehet együtt használni.

__global__ specifikáló jelöli a sejtmagba, és a megfelelő funkciót kell vissza értéket típusú void.

__global__ void RENDSZERMAGKONFIG (float * a, float * b, float * c)

int index = threadIdx.x;

Az elvégzett feladatok a GPU (__device__ és __global__) A következő korlátozások érvényesek:

Hogy meghatározza a helyét a memóriában a GPU változók, az alábbi előírást - __device__, __constant__ és __shared__. Használatuk is ró számos korlátozás:

A nyelv a következő speciális változó egészül ki:

  • gridDim - grid'a mérete (típusú dim3)
  • blockDim - blokkméret (típusú dim3)
  • blockIdx - az aktuális blokk index grid'e (rendelkezik típus uint3)
  • threadIdx - az index a jelenlegi szál a blokk (a típus uint3)
  • warpSize - warp'a mérete (egyfajta int)

A nyelv hozzáadott 1/2/3/4-dimenziós vektort az alaptípus - KAR1, KAR2, char3, char4, uchar1, uchar2, uchar3, uchar4, short1, short2, short3, short4, ushort1, ushort2, ushort3, ushort4, Int1 , int2, int3, int4, uint1, uint2, uint3, uint4, long1, long2, long3, long4, ulong1, ulong2, ulong3, ulong4, float1, float2, float3, float2, és double2.

Kapcsolatfelvétel a komponensek a vektort a neveket - x, y, z és w. Hogy megteremtse a vektorok értékei adott típus a tervezési típus make_.

int2 a = make_int2 (1, 7);

float3 u = make_float3 (1, 2, 3.4f);

Felhívjuk figyelmét, hogy az ilyen típusú (szemben az árnyékoló nyelv GLSL / Cg / HLSL) nem támogatott vektor komponenstudatos műveletet, azaz nem lehet csak úgy hozzá két vektor segítségével az üzemeltető „+” - egyértelműen szükség van, hogy nem az egyes komponenseket.

Is szolgál, hogy meghatározza a dimenzió típusú DIM3 alapján uint3 típus, de van egy normális kivitelező inicializálja az összes komponenst nem definiált egységet.

kernel hívás irányelv

Ahhoz, hogy a kernel elinduljon a GPU használja a következő design:

kernelName <<>> (args)

Szintén a C nyelv hozzáadott __syncthreads funkció szinkronizálja az összes szálak a blokk. ki belőle ellenőrzés kerül vissza csak akkor, ha minden szál egy adott blokk fog okozni ez a funkció. Ie amikor az összes kódot, hogy megy, mielőtt ez a hívás már (és ezért nyugodtan számíthat az eredmények). Ez a funkció nagyon kényelmes a szervezet bezkonfliktnoy munkát a megosztott memóriát.

CUDA támogatja az összes matematikai függvények a szabványos C könyvtár, de a teljesítmény jobb felhasználása a float-analógok (nem dupla) - például Sinf. Ezen túlmenően, CUDA biztosít egy további matematikai függvények (__sinf, __powf stb) biztosít kisebb pontossággal, de lényegesen nagyobb teljesítményt Sinf, powf stb

Basics CUDA host API

CUDA API CPU (host) jelenik meg két formában - egy alacsony szintű CUDA drievr API és CUDA futásidejű API (keresztül valósul CUDA vezető API). Az alkalmazásban, akkor csak az egyiket, akkor nézzük meg CUDA futási API, az egyszerűbb és kényelmesebb.

Minden funkció CUDA vezető API cu előtaggal kezdődnek, és az összes funkcióját CUDA futási API előtaggal kezdődnek CUDA. Mindegyik API-nak a mag bizonyos alapvető funkciókat, mint például az összes rendelkezésre álló eszközök (GPU), dolgozó kontextusok és a szálak, a munka a GPU memória, kölcsönhatás OpenGL és D3D (csak a támogatott a 9. DirectX verzió).

CUDA futási API nem igényel explicit inicializálás - ez automatikusan megtörténik, ha az első hívást, annak bármely funkciókat. Fontos szempont a munka, CUDA, hogy sok API funkciók aszinkron, azaz a vezérlés visszatér, mielőtt a tényleges befejezése a szükséges műveletet.

Között aszinkron műveleteket tartalmazza:

  • core elindítása
  • memória másolás funkció, amelynek elnevezése a Async
  • Memória másolás funkció eszköz <-> eszköz
  • memória inicializálása funkciót.

CUDA támogatja a szinkronizálást keresztül folyamok (stream) - az egyes áramlási meghatározza a végrehajtott műveletek sorozata szigorú sorrendben. Amikor ez a műveletek sorrendjét a különböző adatfolyamok nem szigorúan meghatározott, és változhat.

Minden funkció CUDA API (kivéve a kernel elindul) visszatér cudaError_t típusát. Ha sikeres, akkor a függvény cudaSuccess, különben vissza hibakódot.

Kap egy leírást a hiba formájában a kód vonal lehet a funktsiicudaGetErrorString:

char * cudaGetErrorString (cudaError_t kód);

Azt is, hogy az utolsó hibakód használatával funktsiicudaGetLastError:

Kérjük, vegye figyelembe, hogy az aszinkron végrehajtása hány hívást kap a hibakód jobb használni a funkciót cudaThreadSynchronize, mely várja a befejezése a GPU-minden továbbított kérések és hibaüzenetet küld, ha egy ilyen kérelem eredményeként a hibát.

Munka memória CUDA

A legegyszerűbb módja annak, hogy kiosztani és a szabad memória (beszélünk kizárólag a GPU memória, csak lineáris memória, a másik típus - CUDA-tömbök lesz szó a következő cikkben) használata cudaMalloc funkciók cudeMallocPitch és cudaFree.

float * devPtr; // mutató készülék memóriájába

// kiosztani lineáris memória 256 úszik

cudaMalloc ((void **) devPtr, 256 * sizeof (float));

Kijelölésének CUDA képesség fogalmát használja Compute Capability kifejezve egy pár szám - MAJOR.MINOR. Az első szám utal a globális építészeti változatát, a második - egy kis változás. Mivel a GPU GeForce 8800 Ultra / GTX / GTS egyenlő számítási képesség 1,0, GPU GeForce 8800 GT / GS és a GeForce 9600 GT egyenlő számítási képesség 1.1, GPU GeForce GTX 260 és GTX 280 egyenlő számítási képesség 1.3.

Compute Capability 1.1 támogatja az atomi műveleteket 32 ​​bites szó a globális memória Compute Capability 1.2 támogatja az atomi műveleteket az osztott memóriát és az atomi műveleteket 64 bites szó a globális memória Compute Capability 1.3 támogatja műveleteket a kettős típusú számokat.

Az alábbiakban a forráskód egy egyszerű program, amely felsorolja az összes rendelkezésre álló GPU és azok főbb jellemzőit.

int main (int argc, char * argv [])

printf ( "Talált eszközök \ n.", deviceCount);

A (int eszköz = 0; eszközt

cudaGetDeviceProperties ( devProp, eszköz);

printf ( "Device \ n.", eszköz);

printf ( "számítási képesség .... \ n", devProp.major, devProp.minor);

printf ( "Név% s \ n.", devProp.name);

printf ( "Total Global Memory \ n ..", devProp.totalGlobalMem);

printf ( "Osztott memória blokkonként: \ n.", devProp.sharedMemPerBlock);

printf ( "regiszterek blokkonként \ n ..", devProp.regsPerBlock);

printf ( "warp mérete \ n ..", devProp.warpSize);

printf ( "Max szálak blokkonként \ n ..", devProp.maxThreadsPerBlock);

printf ( "Total konstans memória \ n ..", devProp.totalConstMem);

Tekintsünk néhány egyszerű példát a CUDA bizonyítani az alapvető technikákat a munka vele. A legegyszerűbb példa egy egyszerű növekedése minden elem egydimenziós tömb egy - incr.cu. programot

__global__ void incKernel (float * adatok)

A legegyszerűbb módja van elrendezve nucleus - minden egyes szál megfelel annak a menetnek blokkok és rács-dimenziós. A mag (incKernel funkció) bemenet kap csak egy mutató a tömb adatok a globális memória. Célzása magja - a threadIdx blockIdx, és melyik elem felel meg a menet, és növelje a nevét.

Mivel mind a blokkokat, és az egydimenziós rács, a fonal számát definiáljuk a blokk számát szorozva a szálak száma a blokkban, plusz szál száma a blokkon belül, azaz blockIdx.x * blockDim.x + threadIdx.x.

Fő jellemzője egy kicsit bonyolultabb -, meg kell készítenie egy sor adat a memóriában a CPU, akkor kell használni a cudaMalloc memóriát kiosztani egy példányát a tömb adatok a globális memória (DRAM GPU). Ezután az adatokat átmásolja cudaMemcpy funkciót a CPU memória a globális memória GPU.

Befejezése után az adatok másolása a globális memória lehet kezdeni az adatok kernel után elhívták másolja vissza a számítási eredményeket a GPU globális memória CPU memória.

Szorzás Két mátrix - a legegyszerűbb megközelítés

A következő példa a bonyolultabb (és sürgős) - nézzük meg a használatát CUDA számára megszorozzuk két szögletes mátrixok mérete N * N.

Tegyük fel, hogy két szögletes A és B mátrix mérete N * N (feltesszük, hogy N a 16 többszöröse). A legegyszerűbb kiviteli alak használ egy szál minden egyes eleme a kapott mátrix C, ahol a menet letölti az összes szükséges elem a globális memória, és végrehajtja a szükséges számításokat.

Element ci, j a termék a két A és B mátrix kiszámítása a következő pszeudokód fragmens:

Ezáltal kiszámításához egyetlen elem a mátrix termék kell végezni 2 * N számtani és 2 * N értékeket a globális memória. Nyilvánvaló, hogy ebben az esetben a fő korlátozó tényező a hozzáférés sebessége a globális memória, ami nagyon alacsony. Hogy egyes szálak blokkokba csoportosíthatók nem fontos, és nincs jelentős hatása a teljesítmény, ami ebben az esetben is nagyon alacsony.

Az alábbiakban egy listát a megfelelő programot.

Szorzás két mátrixok segítségével osztott memóriás

Jelentősen javítja a teljesítményt, mint a programok segítségével a megosztott memóriát. Ehhez osszuk el a kapott mátrix be submatrices 16 * 16, a számítást minden ilyen almátrix is ​​részt egy blokkban. Felhívjuk figyelmét, hogy a számítás egy ilyen részmátrixának kell csak kis „banda” a mátrixok és B

Sajnos teljesen lemásolni ezeket a „csík” a megosztott memória gyakorlatilag lehetetlen, mert a viszonylag kis mennyiségű megosztott memóriát. Ezért lehetséges, hogy másképp - szét a „csík” a mátrix 16 * 16 és a számítás az al-mátrixot a mátrix termék kerül sor N / 16 lépéseket.

Ebből a célból, tudomásul vesszük, hogy a számítás az elem ci, j lehet újraírni az alábbiak felhasználásával partíció bár a négyzet részmátrixot

a lépés 0..n / 16:

c [i] [j] + = a [i] [k + lépésben * 16] * b [K + lépésben * 16] [j]

Megjegyezzük, hogy minden egyes értékére a lépés értéke a A és B mátrix veszünk két submatrices a mérete 16 * 16. Tény, hogy a zenekar a rizst. 4 egyszerűen osztva négyzetes almátrix és mindegyik érték megfelel egy lépésben az ilyen almátrix A és egy almátrix B.

Minden lépésnél azt betölteni a megosztott memória egy 16 * 16 részmátrixának A és egy 16 * 16 részmátrixának B. Ezután kiszámítjuk a megfelelő mennyiségű őket elemei a terméket, majd helyezze be a következő 16 * 16 submatrices stb

Minden lépésnél, az egyik szál terhelés pontosan egy eleme az egyes A és B mátrix és összegét számítja ki a megfelelő tagjai. Ha minden a számításokat végzett felvétel eleme a végső mátrix.

Felhívjuk figyelmét, hogy a letöltés után az elemek és B szinkronizálás szükségességét szálak hívja __synchronize a pillanatban az elején települések minden szükséges elem (letölthető maradék blokk menet), akkor már betöltött. Hasonlóképpen, a kezelés végén adtak submatrices, és szükség van a szinkronizálás letöltés előtt ezeket is (hogy győződjön meg arról, hogy a jelenlegi 16 * 16 részmátrixának már nem szükséges és a lehetséges új terhelés).

A következőkben a megfelelő forráskód.

  • Labdarúgás vezetők 69
    Mindent a Football Manager
    • News 64 FM
      projektek News
    • Fejlesztés 66
      Mi jelenleg a projektek megvalósítása
    • terveket 5
      Projekttervek, mit kell tenni, és egyszerűen tervek
  • A fejlesztés 75
    Fejlemény, hogy egykor, vagy nem, valamint a gondolatok és tudomásul veszi.
    • Web 68
      Web design
    • szoftverek 7
      Fejlesztési programok, programmulek, funkciók vagy blokkokat.
    • megjegyzi 9
      Különféle megjegyzések, ötletek projektek.
    • Egyéb 1
      Minden, ami nem tartozik az előző oszlopban
  • News 24
    Hírek a világ tények.
  • Soft-felülvizsgálat 28
    Felülvizsgálata érintő programok eszembe
  • Kemény Áttekintés 3
    Áttekintés a vas a világban
  • Programozási 8
    Különböző cikkek programozás, alkalmazásfejlesztés.

címkefelhő

Cuda - alapjai példák

KarpOlya - jegyzetek a tanár, a biológia és a kémia

Cuda - alapjai példák

Online Football Manager

Kapcsolódó cikkek