GPGPU
General-purpose computing on graphics processing units (GPGPU, de asemenea referit ca GPGP și mai puțin ca GP²) este tehnica de utilizare a unui GPU, care de obicei manevrează calculul doar pentru grafica pe calculator, pentru a efectua calcul în aplicații tratate de obicei de microprocesor. Acest lucru este posibil prin adăugarea de etape de programare și aritmetică de mare precizie la pipeline-urile de randare, ceea ce permite dezvoltatorilor software să utilizeze procesarea în flux asupra datelor non-grafice.
Îmbunătățiri ale GPU
[modificare | modificare sursă]Funcționalitatea GPU a fost, în mod tradițional, foarte limitată. De fapt, timp de mai mulți ani GPU a fost utilizat doar pentru a accelera anumite părți din pipeline-ul grafic. Unele îmbunătățiri au fost necesare înainte ca GPGPU să devină practicabil.
Programabilitate
[modificare | modificare sursă]Vertexul programabil și shaderul de fragment programabil au fost introduși la pipeline-ul grafic pentru a permite programatorilor de jocuri să genereze efecte și mai realiste. Shaderele vertex îi permit programatorului să modifice atributele per-vertex, cum ar fi poziția, culoarea, coordonatele de textură și vectorul normal. Shaderele de fragment sunt utilizate pentru a calcula culoarea unui fragment, sau per-pixel. Shaderele de fragment programabile permit programatorului să înlocuiască, de exemplu, un model iluminat în locul celor furnizate implicit de către placa grafică, de obicei Gouraud shading simplă. Shaderele au permis programatorilor grafici să creeze efecte de lentilă, mapare de deplasare și adâncime de câmp.
Tipuri de date
[modificare | modificare sursă]Plăcile grafice DirectX 9 suportau doar tipuri de culori întregi sau paletate. Diferite formate sunt disponibile, fiecare conținând un element roșu, un element verde și un element albastru. Câteodată o valoare alpha suplimentară este adăugată, pentru a fi utilizată pentru transparență. Formatele obișnuite sunt :
- 8 biți per pixel - Modul paletă, unde fiecare valoare este un index într-un tabel cu valoarea culorii reale specificată într-unul din celelalte formate. Posibil doi biți pentru roșu, trei biți pentru verde, și trei biți pentru albastru.
- 16 biți per pixel - De obicei alocați ca cinci biți pentru roșu, șase biți pentru verde, și cinci biți pentru albastru.
- 24 biți per pixel - opt biți pentru fiecare din roșu, verde, și albastru
- 32 biți per pixel - opt biți pentru fiecare din roșu, verde, albastru, și alpha
Pentru grafica de început cu funcție fixă sau cu programabilitate limitată (adică până la și incluzând DirectX 8.1-GPU conform) a fost suficientă deoarece aceasta este de asemenea reprezentarea utilizată în afișare. Această reprezentare are totuși anumite limitări. Având putere de procesare grafică suficientă, chiar și programatorii grafici doresc să utilizeze formate mai bune, cum ar fi formatele de date în virgulă mobilă, pentru a obține efecte cum ar fi imagistica de gamă dinamică extinsă. Multe aplicații GPGPU necesită precizie în virgulă mobilă, care vine cu plăcile grafice în conformitate cu specificațiile Direct9 X.
Modelul 2.x Shader DirectX 9 a sugerat suport pentru două tipuri de precizie: precizie totală și parțială. Suportul pentru precizia totală poate fi FP32 sau FP24 (virgulă mobilă pe 24 de biți per componentă) sau mai mare, iar precizia parțială a fost FP16. Seria ATI R300 de GPU a suportat precizia FP24 doar în pipeline-ul de fragment programabil (deși FP32 a fost suportat pe procesoarele vertex), în timp ce seria Nvidia NV30 a suportat și FP16 și FP32; alți vânzători cum ar fi S3 Graphics și XGI au suportat o mixtură de formate de până la FP24.
Implementările în virgulă mobilă pe GPU-urile NVidia sunt în mare majoritate conform IEEE; totuși, acest lucru nu se întâmplă pentru toți vânzătorii.[1] Aceasta are implicații pentru corectitudine care este considerată importantă pentru anumite aplicații științifice. În timp ce valorile virgulei mobile pe 64 de biți (mobilă în dublă precizie) sunt disponibile de obicei pe microprocesoare, acestea nu sunt mereu suportate pe GPU-uri; anumite arhitecturi GPU sacrifică conformitatea cu IEEE în timp ce altele duc lipsă de precizie dublă. Au existat eforturi de a imita valorile virgulei mobile în dublă precizie pe GPU-uri; totuși, compromisul de viteză neagă orice beneficiu de a scăpa de calcul pe GPU de la bun început.[2]
Majoritatea operațiilor de pe GPU funcționează în mod vectorial: o singură operație poate fi executată cu până la patru valori simultan. De exemplu, dacă o culoare <R1, G1, B1> trebuie să fie modulată de culoarea <R2, G2, B2>, GPU poate determina culoarea rezultată <R1*R2, G1*G2, B1*B2> printr-o singură operație. Această funcționalitate este utilă în grafică deoarece aproape fiecare tip de date de bază este un vector (de 2,3, sau 4 dimensiuni). Exemplele includ noduri, culori, vectori normali, și coordonate de textură. Multe aplicații pot gestiona acest lucru într-un mod util, și datorită performanței sporite, instrucțiunile vector (SIMD) au fost mult timp disponibile pe microprocesoare.
În luna Noiembrie 2006 NVidia a lansat CUDA, un SDK și un API care permit unui programator să utilizeze limbajul de programare C pentru a crea algoritmi pentru execuție pe GPU-urile seriei Geforce 8. AMD oferă un similar SDK+API pentru GPU-urile bazate pe ATI, acest SDK și tehnologia sunt denumite FireStream SDK (inițial o interfață hardware subțire numită Close to Metal), proiectat să concureze direct cu NVidia CUDA. OpenCL de la Khronos Group este folosit în asociere cu OpenGL pentru a unifica extensia limbajelor C între arhitecturi diferite; acesta suportă NVidia și GPU-uri AMD/ATI, cât și microprocesoare de uz general. GPGPU comparat, de exemplu, cu acceleratorii tradiționali în virgulă mobilă cum ar fi plăcile CSX700 pe 64 de biți de la ClearSpeed care sunt utilizate în prezent în supercalculatoare, GPU-uri curente de top de la NVidia și AMD evidențiază calculul de precizie unică pe 32 de biți; calculul de dublă precizie pe 64 de biți execută mult mai încet.
Concepte de programare GPGPU
[modificare | modificare sursă]GPU-urile sunt proiectate în mod specific pentru grafică și de aceea sunt foarte restrictive în termeni de operații și programare. Datorită naturii lor, GPU-urile sunt eficiente doar pentru abordarea problemelor care pot fi rezolvate utilizând procesarea în flux și hardware ce poate fi folosit în anumite moduri.
Procesarea în flux
[modificare | modificare sursă]GPU-urile pot procesa doar noduri și fragmente independente, dar poate procesa multe din ele în paralel. Acest lucru este eficient în mod deosebit atunci când programatorul dorește să proceseze multe noduri și fragmente în același mod. În acest sens, GPU-urile sunt procesoare în flux - procesoare care pot opera în paralel prin rularea unui singur nucleu în același timp pe mai multe înregistrări dintr-un flux.
Un flux este un set simplu de înregistrări care necesită calcul similar. Fluxurile furnizează paralelism de date. Nucleele (în engleză kernel) sunt funcțiile care sunt aplicate fiecărui element din flux. În GPU-uri, nodurile și fragmentele sunt elementele din fluxuri și shaderele vertex și fragment sunt nucleele pe care sunt rulate acestea. Deoarece GPU-urile procesează elemente independent nu există posibilitatea de a avea date partajate sau statice. Pentru fiecare element se poate citi de la intrare, se poate executa operații pe el, și se poate afișa la ieșire. Este permis să se aibă intrări și ieșiri multiple, dar niciodată o parte de memorie care poate fi și citită și scrisă în același timp.
Intensitatea aritmetică este definită ca numărul de operații efectuate per cuvânt de memorie transferată. Este important pentru aplicațiile GPGPU să aibă intensitate aritmetică înaltă altfel latența de acces la memorie va limita accelerarea calculului.[3]
Aplicațiile GPGPU ideale au seturi mari de date, paralelism de nivel înalt, și dependență minimă între elementele de date.
Concepte de programare GPGPU
[modificare | modificare sursă]Resurse de calcul
[modificare | modificare sursă]Există o varietate de resurse de calcul disponibile pe GPU:
- Procesoare de programare - Pipeline-urile vertex, primitive și de fragment permit programatorului să execute nucleul pe fluxuri de date
- Rasterizare - creează fragmente și interpolează constante per-vertex cum ar fi coordonate de textură și culoare
- Unitate de textură - interfață de memorie doar citire
- Buffer de cadre - interfață de memorie doar scriere
De fapt, programatorul poate înlocui o textură doar scriere pentru ieșire în loc de buffer-ul de cadre. Aceasta este realizată fie prin Randare de textură (în engleză Render to Texture (RTT)), Render-To-Backbuffer-Copy-To-Texture (RTBCTT), sau prin cel mai recent flux de ieșire.
Texturile ca flux
[modificare | modificare sursă]Cea mai uzuală formă pentru un flux de a prelua GPGPU este o rețea 2D deoarece aceasta se încadrează natural cu modelul de randare construit în GPU-uri. Multe calcule mapează în rețele: algebra matricială, procesarea de imagini, simularea bazată pe fizică, și așa mai departe.
Din moment ce texturile sunt folosite ca memorie, căutările de textură sunt folosite ca citiri de memorie. Anumite operații pot fi realizate automat de către GPU datorită acestui fapt.
Nuclee
[modificare | modificare sursă]Nucleele pot fi considerate ca fiind corpul buclei. De exemplu, dacă programatorul operează pe o rețea de pe microprocesor, codul ar arăta astfel:
// Rețelele de intrare și ieșire au 10000 x 10000 sau 100 milioane de elemente. void transform_10k_by_10k_grid(float in[10000][10000], float out[10000][10000]) { for(int x = 0; x < 10000; x++) { for(int y = 0; y < 10000; y++) { // The next line is executed 100 million times out[x][y] = do_some_hard_work(in[x][y]); } } }
În GPU, programatorul specifică doar corpul buclei ca nucleu și ce date trebuie utilizate de procesarea geometrică invocată.
Controlul fluxului
[modificare | modificare sursă]În codul secvențial este posibil să se controleze fluxul programului utilizând sintaxe if-then-else și forme variate de bucle. Asemenea structuri de control al fluxului au fost adăugate de curând la GPU-uri.[4] Scrierile condiționale ar putea fi terminate utilizând o serie de operații aritmetice pe bit în mod corespunzător, dar branșamentele de buclă și condiționale nu au fost posibile.
GPU-urile recente permit branșament, dar de obicei cu o penalizare de performanță. Branșamentul ar trebui evitat în general în buclele interne, indiferent de microprocesor sau codul GPU, și tehnici variate, cum ar fi rezoluția static de ramură, pre-calcul, și Z-cull[5] pot fi utilizate pentru a realiza branșamentul atunci când suportul hardware nu există.
Tehnici GPU
[modificare | modificare sursă]Maparea
[modificare | modificare sursă]Operația de mapare aplică funcția dată (nucleul) fiecărui element din flux. Un exemplu simplu este multiplicarea fiecărei valori din flux cu o constantă (crescând luminozitatea unei imagini). Operația de mapare este simplu de implementat pe un GPU. Programatorul generează un fragment pentru fiecare pixel de pe ecran și aplică un program fragment fiecăruia. Fluxul rezultat de aceeași dimensiune este stocat în bufferul de ieșire.
Reducerea
[modificare | modificare sursă]Anumite calcule necesită un flux mai mic (posibil un flux de 1 element) de la un flux mai mare. Aceasta se numește reducere a fluxului. În general o reducere poate fi realizată în mai mulți pași. Rezultatele de la pasul anterior sunt utilizate ca intrare pentru pasul curent și intervalul peste care operația este aplicată este redus până când rămâne un singur element de flux.
Filtrarea fluxului
[modificare | modificare sursă]Filtrarea fluxului este în esență o reducere ne-uniformă. Filtrarea implică înlăturarea elementelor din flux pe baza unui criteriu.
Dispersia
[modificare | modificare sursă]Operația de dispersie este cel mai firesc definită pe procesoarele vertex. Procesorul vertex este capabil să ajusteze poziția vertexului, fapt ce permite programatorului să controleze unde informația este localizată în rețea. Alte extensii sunt de asemenea posibile, cum ar să se controleze cât de mare este aria pe care vertexul o influențează.
Procesorul de fragment nu poate realiza o operație de despersie directă deoarece locația fiecărui fragment din reȚea este fixată la momentul creării fragmentului și nu poate fi modificată de către programator. Totuși, o operație logică de dispersie poate fi câteodată returnată sau implementată cu un pas suplimentar de adunare. O implementare de dispersie va emite la început o valoare de ieșire și o adresă de ieșire. O operație de adunare ce urmează imediat după utilizează comparatori de adresă pentru a vedea dacă valorile de ieșire se mapează la slotul de ieșire curent.
Adunarea
[modificare | modificare sursă]Procesorul de fragment este capabil să citească texturi într-un mod de acces aleatoriu, astfel încât poate aduna informații din orice celulă din rețea, sau din celule multiple din rețea, dupa preferințe.
Sortarea
[modificare | modificare sursă]Operația de sortare transformă un set neordonat de elemente într-un set ordonat de elemente. Implementarea cel mai des întâlnită pe GPU-uri este utilizând rețele de sortare.[5]
Căutarea
[modificare | modificare sursă]Operația de căutare permite programatorului să găsească un element particular din flux, sau să găsească vecini posibili ai unui element specificat. GPU nu este utilizat să grăbească căutarea unui element individual, dar în schimb este folosit să realizeze căutări multiple în paralel.
Structuri de date
[modificare | modificare sursă]O mulțime de structuri de date poate fi reprezentată pe GPU:
- Mulțimi dense
- Mulțimi rare – statice sau dinamice
- Structuri adaptive
Note
[modificare | modificare sursă]- ^ Mapping computational concepts to GPUs: Mark Harris. Mapping computational concepts to GPUs. In ACM SIGGRAPH 2005 Courses (Los Angeles, California, July 31 – 4 august 2005). J. Fujii, Ed. SIGGRAPH '05. ACM Press, New York, NY, 50.
- ^ Double precision on GPUs (Proceedings of ASIM 2005) Arhivat în , la Wayback Machine.: Dominik Goddeke, Robert Strzodka, and Stefan Turek. Accelerating Double Precision (FEM) Simulations with (GPUs). Proceedings of ASIM 2005 – 18th Symposium on Simulation Technique, 2005.
- ^ Asanovic, K., Bodik, R., Demmel, J., Keaveny, T., Keutzer, K., Kubiatowicz, J., Morgan, N., Patterson, D., Sen, K., Wawrzynek, J., Wessel, D., Yelick, K.: A view of the parallel computing landscape. Commun. ACM 52(10) (2009) 56–67
- ^ „GPU Gems - Chapter 34, GPU Flow-Control Idioms”. Arhivat din original la . Accesat în .
- ^ a b GPGPU survey paper Arhivat în , la Wayback Machine.: John D. Owens, David Luebke, Naga Govindaraju, Mark Harris, Jens Krüger, Aaron E. Lefohn, and Tim Purcell. "A Survey of General-Purpose Computation on Graphics Hardware". Computer Graphics Forum, volume 26, number 1, 2007, pp. 80-113.