[Pullquote align = "left | centru | dreapta" textalign = "left | centru | dreapta" width = "30%"] Nucleele nu este niciodată prea mult ... [/ pullquote]
GPU Modern - aceasta monstruoase fiare agile, capabile de a mesteca pe gigabytes de date. Cu toate acestea, o persoană este viclean și, întrucât nu a crescut puterea de calcul, vine cu sarcina mai mult și mai dificil, asa ca vine un moment când trebuie să observăm cu tristețe - optimizare nevoie 🙁
Acest articol descrie conceptele de bază, în scopul de a face mai ușor pentru a naviga teoria gpu-optimizare și regulile de bază a acestor concepte, a trebuit să merg în mai puțin.
Motivele pentru care unitatea de procesare vizuală eficientă pentru cantități mari de date care urmează să fie prelucrate:
- ei au mai multe oportunități pentru executarea în paralel a sarcinilor (multe, multe procesoare)
- lățime mare de bandă a memoriei
Lățime de bandă memorie (lățime de bandă de memorie) - acest lucru este cât de multe informații - biți sau gigaocteți - pot fi transmise pe unitatea de timp sau un al doilea ciclu de procesare.
Una dintre problemele de optimizare - utilizează capacitatea maximă - pentru a crește tranzitată de performanță (în mod ideal, ar trebui să fie egală cu lățimea de bandă de memorie).
Pentru a îmbunătăți utilizarea lățimii de bandă:
- crește cantitatea de informații - folosesc pasajul din plin (de exemplu, fiecare fir funcționează cu float4)
- reduce latența - întârzierea dintre operațiunile
Delay (latenta) - perioada de timp între momentul în care operatorul a solicitat o anumită
celule de memorie și momentul în care este pus la dispoziție datele la procesor pentru a executa instrucțiuni.
Chiar întârzierea nu ne poate afecta - aceste restricții sunt prezente în hardware-ul.
Este din cauza acestei întârzieri, procesorul poate servi simultan mai multe fire -
până când debitul și cererea să-i ofere cu memorie fluxul B poate avea ceva de conta și fluxuri cu așteptați să vină la datele solicitate.
Cum de a reduce întârzierea (latență) în cazul în care se utilizează sincronizarea:
- reduce numărul de fluxuri în bloc
- creșterea numărului de grupuri de blocuri
Utilizarea resurselor GPU pentru întregul - gradul de ocupare GPU
Puterea de calcul GPU - sute de calcule-procesor e foame când se creează programul - miezul (kernel) - umerii programatorului stabili sarcina de echilibrare a sarcinii pe ele. Eroare poate duce la faptul că cele mai multe dintre aceste resurse prețioase pot fi inactiv fără țintă. Acum, voi explica de ce. Trebuie să înceapă de departe.
Permiteți-mi să vă reamintesc că urzeală (urzeală în termeni NVidia, Wavefront - terminologia AMD) - un set de fire care efectuează simultan aceeași funcție pe procesor-kernel. Fluxurile combinate în blocuri împărțite de programator pe planificatorul fir de urzeală (separat pentru fiecare multiprocesor) - până una ruleaza urzeală, o a doua memorie de așteptare pentru a procesa cereri etc. Dacă unele dintre firul de urzeală încă efectua calcule, iar alții au făcut deja tot ce au putut - un loc de a fi o utilizare ineficientă a resurselor de calcul - popular denumit capacitatea neutilizată.
Fiecare punct de sincronizare, fiecare logică de ramificare poate crea o situație de inactivitate. Divergența maximă (execuție logica ramificare) depinde de mărimea urzelii. Pentru GPU de la NVidia - este de 32 pentru AMD - 64.
Pentru a reduce un simplu urzeală multiprocesor de rulare:
- minimiza de așteptare bariere de timp
- pentru a minimiza diferența în îndeplinirea funcțiilor logice-Kernel
Pentru a rezolva în mod eficient această problemă are sens pentru a înțelege - cum este formarea urzelii (în cazul unei dimensiuni nekolko). De fapt, procedura este simplă - în primul rând pe X, Y și apoi, în ultimă instanță, Z.
nucleu începe cu blocuri de dimensiune 64 x 16, firele de urzeală sunt împărțite în ordinea X, Y, Z - adică primul membru 64 sunt împărțite în două urzeală, apoi al doilea, etc.
Kernel-ul începe cu blocuri de dimensiune 16 × 64. Prima urzeală adăugat la primul și al doilea element 16 în a doua urzeală - a treia și a patra, etc.
Cum de a reduce divergenta (amintiți-vă - ramificări - nu este întotdeauna cauza o pierdere critică a productivității)
- atunci când fluxurile adiacente sunt diferite moduri de execuție - o mulțime de termeni și de clicuri pe ele - să caute modalități de a re-structurare
- Nu căuta un flux de sarcină echilibrată și decisiv pentru a elimina (acest lucru este în cazul în care nu numai că există condiții, rață încă din cauza primul fir de la aceste condiții este întotdeauna ceva pentru a calcula, iar al cincilea în această condiție nu se încadrează, și mers în gol)
Cum se utilizează GPU la maxim
resursele GPU, din păcate, de asemenea, au limitele lor. Și, strict vorbind, înainte de a începe funcția de nucleu, este logic să se definească limitele și aceste limite sunt luate în considerare în distribuția sarcinii. De ce este important acest lucru?
- numărul de fluxuri de blocuri / grupul de lucru trebuie să fie un număr multiplu de procesoare de flux
- mărimea blocului / grup de lucru trebuie să fie un multiplu de urzeală
Ar trebui să se ia în considerare faptul că valoarea minimă absolută - 3-4 urzeală / veyfronta filare simultan pe fiecare procesor, ghiduri înțelept sfătuiți să procedeze din considerente - nu mai puțin de șapte veyfronatov. În același timp, - nu uita limitările hardware-ului!
La capul de toate aceste detalii pentru a păstra plictisește ușor, deoarece pentru calcularea gpu-ocupare NVidia a oferit un instrument neașteptat - (!) Calculator ekselny ambalate cu macro-uri. Acolo puteți introduce informațiile privind numărul maxim de fire pentru SM, numărul de registre și mărimea comune de memorie (partajată) disponibile pe procesoare de stream, precum și parametrii utilizați pentru a iniția o funcție - și oferă o eficiență procent de utilizare a resurselor (și vărsați pe părul de pe cap și dea seama că, în scopul de a utiliza toate miezurile pe care lipsesc registre).
operațiuni de GPU și memorie
Problema în această situație - fiecare cerere returnează un răspuns la dimensiunea datelor dintr-o bucată multiplu de 128 biți. Și fiecare fir folosește doar un sfert din ea (în cazul unei variabile convenționale cu patru octeți). Atunci când fluxurile adiacente funcționează simultan cu datele în serie în celule de memorie - acest lucru reduce numărul total de accesări ale memoriei. Numit acest fenomen - combinate citească și să scrie operațiuni (coagulat de acces - bun atât citească și să scrie!) - și cu organizarea corectă a codului (strided acces la bucată contigue de memorie -! Bad) poate îmbunătăți semnificativ performanța. Atunci când se organizează un kernel - amintiți-vă - acces adiacente - în cadrul elementelor unei singure linii de memorie, de lucru cu elementele coloanei - nu este la fel de eficient. Vrei mai multe detalii? Mi-a plăcut aici pdf - sau Google pentru „tehnici de memorie coalescență“.
Și, în sfârșit, un pic mai mult despre memorie. multiprocesor de memorie partajată este de obicei organizată sub formă de bănci de memorie care conțin 32 de cuvinte biți - date. Numărul de bănci pe buna tradiție variază de la o generație la alta GPU - 16/32 Dacă fiecare fir solicită date într-o bancă separată - toate bune. În caz contrar, se pare că mai multe cereri de acces citire / scriere la aceeași bancă, și avem - un conflict (conflict banca de memorie partajată). O astfel de manipulare a conflictelor sunt serializate și executate secvențial, respectiv, mai degrabă decât în paralel. În cazul în care o bancă pentru a aborda toate firele - utilizarea „difuzare“ răspuns (difuzare) și nu există nici un conflict. Există mai multe modalități de a aborda în mod eficient un conflict de acces, mi-a placut descrierea principalelor metode pentru a scăpa de conflicte de acces la băncile de memorie - aici.
Cum de a face operații matematice chiar mai repede? Amintiți-vă că:
- calcularea dublă precizie - aceasta este o operațiune de încărcare ridicată cu fp64 >> fp32
- Constant tip 3.13 în cod, în mod implicit, este interpretată ca fp64 excepția cazului în care punct în mod explicit 3.14f
- pentru optimizarea matematica nu este de prisos să se ocupe în Hyde - și dacă există steaguri de compilator
- producătorii includ în funcțiile lor SDK, care utilizează dispozitive speciale pentru a obține o performanță (de multe ori - în detrimentul toleranței)
Resturi pentru profilare:
P. S. Ca linii directoare de optimizare mai lungi, pot recomanda googling tot felul de cele mai bune practici de ghid pentru OpenCL si CUDA.