Mi az "egyesítés" a CUDA globális memóriatranzakciójában? Még azután sem értettem, hogy átnéztem a CUDA útmutatómat. Hogyan kell csinálni? A CUDA programozási útmutató mátrixpéldájában a mátrix soronkénti elérését "összevontnak" nevezzük, vagy a col.. by col.. összevonásnak nevezzük? Melyik a helyes és miért?
A CUDA-ban mi az a memóriaegyesítés, és hogyan érhető el?
Válaszok:
Valószínűleg ez az információ csak az 1.x vagy a cuda 2.0 számítási kapacitásra vonatkozik. Az újabb architektúrák és a cuda 3.0 kifinomultabb globális memória-hozzáféréssel rendelkeznek, és valójában az „összevont globális terhelések” még csak nincsenek is profilozva ezekhez a chipekhez.
Ez a logika a megosztott memóriára is alkalmazható a banki konfliktusok elkerülése érdekében.
Az egyesített memória-tranzakció olyan, amelyben a fél-tekervényben lévő összes szál egyszerre éri el a globális memóriát. Ez túl egyszerű, de a helyes módja annak, hogy az egymást követő szálak egymás utáni memóriacímeket érjenek el.
Tehát, ha a 0., 1., 2. és 3. szál a 0x0, 0x4, 0x8 és 0xc globális memóriát olvassa, akkor ennek egy egyesített olvasásnak kell lennie.
Egy mátrixos példában ne feledje, hogy azt szeretné, hogy a mátrix lineárisan legyen a memóriában. Ezt tetszés szerint megteheti, és a memória-hozzáférésnek tükröznie kell a mátrix elrendezését. Tehát az alábbi 3x4-es mátrix
0 1 2 3
4 5 6 7
8 9 a b
sorról sorra meg lehet tenni, így, így (r,c) a memóriába képezi le (r*4 + c)
0 1 2 3 4 5 6 7 8 9 a b
Tegyük fel, hogy egyszer el kell érnie az elemet, és négy szála van. Melyik elemhez mely szálak lesznek felhasználva? Valószínűleg akár
thread 0: 0, 1, 2
thread 1: 3, 4, 5
thread 2: 6, 7, 8
thread 3: 9, a, b
or
thread 0: 0, 4, 8
thread 1: 1, 5, 9
thread 2: 2, 6, a
thread 3: 3, 7, b
Melyik a jobb? Melyik eredményez egyesített olvasást, és melyik nem?
Akárhogy is, minden szál három hozzáférést biztosít. Nézzük meg az első hozzáférést, és nézzük meg, hogy a szálak egymás után hozzáférnek-e a memóriához. Az első opcióban az első hozzáférés 0, 3, 6, 9. Nem egymást követő, nem egyesült. A második lehetőség: 0, 1, 2, 3. Egymás után! Összeállt! Hurrá!
A legjobb módszer valószínűleg az, ha megírja a kernelt, majd profilba állítja, hogy megnézze, vannak-e nem egyesített globális betöltései és tárolói.
A memóriaegyesítés olyan technika, amely lehetővé teszi a globális memória sávszélességének optimális kihasználását. Ez azt jelenti, hogy ha párhuzamos szálak futtatják ugyanazt az utasítást, akkor a globális memória egymást követő helyeihez férnek hozzá, akkor a legkedvezőbb hozzáférési mintát érik el.
A fenti ábra példája segít elmagyarázni az egyesített elrendezést:
Az (a) ábrán n m hosszúságú vektor lineárisan van tárolva. A j vektor i elemét v j i jelöli. A GPU-kernel minden szála egy m hosszúságú vektorhoz van hozzárendelve. A CUDA szálai egy blokktömbbe vannak csoportosítva, és a GPU-ban minden szál egyedi azonosítóval rendelkezik, amely indx=bd*bx+tx
-ként definiálható, ahol a bd
a blokk dimenzióját jelöli, a bx
a blokk indexét és a tx
a szál indexét jelenti minden blokkban.
A függőleges nyilak azt az esetet mutatják, amikor párhuzamos szálak férnek hozzá az egyes vektorok első összetevőihez, azaz a memória 0, m, 2m... címeihez. Amint az (a) ábrán látható, ebben az esetben a memória hozzáférés nem egymást követő. A címek közötti hézag nullázásával (piros nyilak látható a fenti ábrán), a memória-hozzáférés egyesül.
A probléma azonban itt kissé bonyolulttá válik, mivel a GPU blokkonkénti tartózkodási szálak megengedett mérete bd
-ra van korlátozva. Ezért az összevont adatelrendezés történhet úgy, hogy az első bd
vektorok első elemeit egymás utáni sorrendben tároljuk, majd a második bd vektorok első elemeit és így tovább. A többi vektorelemet hasonló módon tároljuk, amint az a (b) ábrán látható. Ha az n (vektorok száma) nem bd
tényező, akkor az utolsó blokk fennmaradó adatait fel kell tölteni valamilyen triviális értékkel, pl. 0.
Az (a) ábrán látható lineáris adattárolóban az indx vektor i összetevője (0 ≤ i ‹ m) em> (0 ≤ indx ‹ n) címét a m × indx +i
; a (b) ábrán szereplő egyesített tárolási mintában ugyanaz a komponens van megcímezve
(m × bd) ixC + bd × ixB + ixA
,
ahol ixC = floor[(m.indx + j )/(m.bd)]= bx
, ixB = j
és ixA = mod(indx,bd) = tx
.
Összefoglalva, a több, m méretű vektor tárolására vonatkozó példában a lineáris indexelés az egyesített indexelésre van leképezve a következőképpen:
m.indx +i −→ m.bd.bx +i .bd +tx
Ez az adatátrendezés a GPU globális memóriájának jelentősen nagyobb memóriasávszélességéhez vezethet.
forrás: "Számítások GPU-alapú gyorsítása nemlineáris végeselem-deformációs elemzésben." Az orvosbiológiai mérnöki numerikus módszerek nemzetközi folyóirata (2013).
Ha egy blokkban lévő szálak egymást követő globális memóriahelyekhez férnek hozzá, akkor a hardver az összes hozzáférést egyetlen kérelemben egyesíti (vagy egyesíti). A mátrix példában a sorban lévő mátrixelemek lineárisan vannak elrendezve, majd a következő sor következik, és így tovább. Például 2x2 mátrix és 2 szál esetén egy blokkban a memóriahelyek a következőképpen vannak elrendezve:
(0,0) (0,1) (1,0) (1,1)
A sorelérésben a szál1 eléri a (0,0) és (1,0) értékeket, amelyeket nem lehet egyesíteni. Az oszlopelérésben a szál1 eléri a (0,0) és a (0,1) értékeket, amelyek egyesíthetők, mivel szomszédosak.
Az összevonás feltételeit a CUDA 3.2 programozási útmutatója szépen dokumentálja., G.3.2. A rövid változat a következő: a warp szálainak sorban kell elérniük a memóriát, és az elérendő szavaknak >=32 bitesnek kell lenniük. Ezenkívül a vetemítés által elért alapcímnek 64, 128 vagy 256 bájtosnak kell lennie a 32, 64 és 128 bites hozzáférésekhez igazítva.
A Tesla2 és a Fermi hardver megfelelő munkát végez a 8 és 16 bites hozzáférések összekapcsolásában, de legjobb elkerülni ezeket, ha csúcs sávszélességre vágynak.
Ne feledje, hogy a Tesla2 és a Fermi hardver fejlesztései ellenére az összevonás SEMMILYEN SZEMÉLYESEN elavult. Még a Tesla2 vagy a Fermi osztályú hardvereken is a globális memóriatranzakciók összevonásának elmulasztása kétszeres teljesítménycsökkenést eredményezhet. (A Fermi osztályú hardvereken ez csak akkor igaz, ha az ECC engedélyezve van. A szomszédos, de össze nem kapcsolt memóriatranzakciók körülbelül 20%-os sikert érnek el a Fermi esetében.)
thread 0: 0, 1, 2 etc...
), ezért most jobbat keresek :-) 19.05.2011nvprof
hatékony eszköznek tűnik, amely az Ön számára is használható. Szeretném hangsúlyozni, hogy a fontos mutatók az eszköz számítási képességétől és a CUDA-verziótól függenek, és anvprof
-nak lehetővé kell tennie ezek bármelyikének megfigyelését. Először állítsa működésbe a kernelt, majd optimalizálja a rendelkezésre álló profilozók bármelyikével. 01.10.2014