Procesor vectorial

Un procesor vectorial este un procesor care are diverse funcționalități arhitecturale, permițându-i să îmbunătățească executarea programelor care utilizează matrici și matrici la scară masivă și care face posibilă exploatarea paralelismului inerent utilizării acestora din urmă.

Dezvoltat pentru aplicații științifice și exploatat de mașinile Cray și de supercomputerele care îl vor urma, acest tip de arhitectură și-a arătat rapid avantajele pentru aplicațiile publice generale (putem cita manipularea imaginilor). Este implementat parțial în procesoarele de consum prin instrucțiuni SIMD , fie datorită unei unități dedicate de calcul vectorial ( AltiVec ), fie simulat de instrucțiuni de tip vector de nivel scăzut ( MMX / SSE ). Spre deosebire de SIMD de tip MMX, unde este necesar să încărcați vectorii în registre în mai multe operații, apoi să executați o operație suplimentară pe registrele sale, într-o unitate vectorică încărcăm adresa unei tabele vectoriale, lățimea vectorilor sau pasul și lungimea tabelului care urmează să fie procesate prin instrucțiuni într-un registru, instrucțiunea vectorului conectează apoi calculul său la toți vectorii acestui tabel.

Joc de instrucțiuni

Procesoarele vectoriale pot fi văzute ca procesoare normale, la care am adăugat o serie de instrucțiuni optimizate pentru manipularea matricelor. Aceste instrucțiuni optimizate pentru tablouri pot fi văzute ca variante ale instrucțiunilor normale, dar optimizate pentru a procesa date mai mari (pentru accesări de memorie) sau capabile să efectueze operațiuni în paralel. Aceste instrucțiuni se numesc instrucțiuni vectoriale. Există mai multe tipuri, pe care le vom prezenta în cele ce urmează.

Instrucțiuni de calcul vector

Instrucțiunile vectoriale sunt instrucțiuni care efectuează mai multe operațiuni în paralel, pe date diferite. Aceste instrucțiuni de calcul vectorial funcționează pe un set de date de aceeași dimensiune și tip, setul formând ceea ce se numește vector. De obicei, acești vectori conțin mai mulți numere întregi sau numere în virgulă mobilă plasate unul lângă celălalt. O instrucțiune de calcul vectorial va procesa fiecare dată a vectorului în paralel, și anume în același timp și independent de celelalte. De exemplu, o instrucțiune de adunare vectorială va însuma împreună datele care se află în același loc în doi vectori și va plasa rezultatul într-un alt vector, în același loc. La executarea unei instrucțiuni asupra unui vector, datele prezente în acel vector sunt procesate simultan.

Operațiunile în cauză pot fi:

Unele instrucțiuni similare sunt disponibile pe unele procesoare non-vectoriale. De exemplu, toate procesoarele x86 moderne conțin extensii la setul de instrucțiuni, cum ar fi MMX sau SSE, care oferă astfel de instrucțiuni de calcul vectorial.

Instrucțiuni de acces la memorie

Orice procesor vectorial are diverse instrucțiuni de acces la memorie, inclusiv cel puțin o instrucțiune pentru citirea vectorilor și alta pentru scriere. Pe procesoarele de vectori mai vechi, vectorii sunt citiți sau scrise direct în memorie. Un astfel de procesor vectorial este denumit memorie-memorie. Acest nume subliniază faptul că vectorii sunt citiți și scrise direct în memoria RAM a computerului, fără stocare intermediară vizibilă în setul de instrucțiuni. Dar acest mod de funcționare pune unele probleme de performanță, având în vedere încetineala accesului la memorie și inutilitatea cache-urilor pe acest tip de arhitectură. Procesoarele vectoriale de tip Load-Store au fost inventate pentru a rezolva această problemă. Acestea au registre de vectori, care fac posibilă stocarea vectorilor în întregime. Un programator poate decide plasarea anumitor vectori în aceste registre: rezultatele intermediare ale calculelor sunt înregistrate și accesate din aceste registre, ceea ce este mai rapid decât să le înregistrați și să le manipulați în memoria RAM. Pentru a utiliza aceste registre de vectori în mod eficient, procesorul trebuie să fie capabil să schimbe vectori între memoria RAM și aceste registre.

Procesoarele vectoriale de stocare a încărcăturii au instrucțiuni capabile să transfere vectori între RAM și registre de vectori. Aceste instrucțiuni sunt instrucțiuni de acces la memorie. Pe procesoarele vectoriale, numai aceste instrucțiuni pot citi sau scrie în memorie: toate celelalte instrucțiuni vectoriale manipulează vectorii plasați în registrele vectoriale. Aceste instrucțiuni au moduri de adresare specializate, prin însăși natura vectorilor. Modul principal de adresare este modul de adresare absolut, adică adresa vectorului manipulat este integrată direct în codul mașinii din instrucțiune, dar sunt disponibile și alte moduri de adresare inspirate de modurile de adresare pe arhitecturi non-vectoriale.

Acces la memorie contiguă

Cu modul de adresare absolută, instrucțiunile pot specifica adresa de memorie a unui vector, care este apoi doar un pachet de date contigu în memorie. De exemplu, dacă procesorul gestionează vectori de 8 octeți, fiecare instrucțiune de acces la memorie utilizând modul de adresare absolută va citi sau scrie blocuri de 8 octeți. Adresa de pornire a acestor blocuri nu este supusă niciunei constrângeri de aliniere, ceea ce nu este cazul procesorelor moderne care utilizează seturi de instrucțiuni precum SSE, MMX etc. Motivul pentru aceasta este că gestionarea acceselor de memorie nealiniată face ca circuitele de citire / scriere a memoriei să fie mai complexe. Pe de altă parte, aceste constrângeri complică utilizarea instrucțiunilor vectoriale. De exemplu, un compilator va avea mai greu timp folosind instrucțiuni de calcul vectorial în prezența constrângerilor de aliniere.

Acces la pas și Scatter-Gather

Pe un procesor vector, sunt disponibile alte moduri de încărcare și salvare a vectorilor. Putem menționa în mod deosebit existența acceselor de memorie cu pași mari și împrăștiere. Aceste accesări permit unei instrucțiuni de încărcare a datelor împrăștiate în memorie pentru a le asambla într-un vector.

Accesul la pas vă permite să încărcați sau să salvați date vectoriale care sunt separate printr-un interval regulat de adrese. O instrucțiune de acces la memorie care dorește să utilizeze acest mod de acces trebuie să cunoască adresa inițială, cea a primului element al vectorului și distanța dintre două date din memorie. Acest mod de acces permite instrucțiuni pentru a gestiona mai bine tablourile de structuri, precum și tablourile multidimensionale. Când se utilizează astfel de matrice, se întâmplă destul de des să accesăm doar anumite elemente, toate separate de aceeași distanță. De exemplu, dacă facem calcule geometrice în spațiu, este foarte bine să procesăm doar coordonatele de pe axa x, fără acces pe axa y sau axa z. Instrucțiunile de acces la memorie Stride permit procesorului să gestioneze astfel de cazuri eficient.

Ultimul tip de acces: Scatter-Gather. Acest acces este utilizat pentru a gestiona mai bine matricile rare. În aceste matrice, o mare parte a elementelor sunt zero. Din motive de optimizare, numai elementele diferite de zero ale matricei sunt stocate în memorie. Cu acest tip de organizare, instrucțiunile vectoriale nu ar putea fi utilizate pe acest tip de matrice, fără Scatter-Gather. Accesele Scatter-Gather pot fi văzute ca o generalizare a adreselor indirecte de registre către vectori. Cu acest acces, adresele fiecărui element al vectorului sunt stocate într-un registru vector. Accesul Scatter-Gather vă va permite să citiți sau să scrieți la diferitele adrese adunate în acest vector.

Registrele procesorului vectorial

Așa cum s-a descris mai sus, pe unele procesoare, vectorii sunt stocați în registre de vectori pentru eficiență. Aceste registre au toate o dimensiune fixă. Aceste registre au o dimensiune care variază între 64 și 256 de biți, pentru cele mai comune dimensiuni.

În general, aceste registre nu sunt specializate: pot stoca numerele întregi și plutesc indiferent. Și conținutul lor se adaptează la dimensiunea lor. Adică un registru pe 128 de biți poate stoca indiferent:

  • 8 numere întregi de 16 biți;
  • 8 plutitoare pe 16 biți;
  • 4 numere întregi de 32 de biți;
  • 4 plutitoare pe 32 de biți;
  • 2 numere întregi de 64 de biți;
  • 2 plutitoare de 64 de biți;
  • etc.

Un procesor vectorial poate încorpora și alte registre pentru a facilita prelucrarea diferitelor instrucțiuni. Aceste registre vor permite compilatorului să utilizeze mai bine instrucțiunile vectoriale pentru a compila anumite structuri software. Printre aceste registre, pot fi menționate registrul de lungime vectorială și registrul de mască vectorială .

Vectorizare

Utilizarea instrucțiunilor vectoriale face posibilă facilitarea anumitor tratamente pe tablouri. În prezent, aceste instrucțiuni sunt dificil de utilizat în limbaje de nivel înalt și este la latitudinea compilatorului să traducă anumite tratamente pentru tablouri în instrucțiuni vectoriale. Aceste transformări care fac posibilă traducerea bucăților de programe în instrucțiuni vectoriale se numesc vectorizare.

Buclă de desfacere

Cea mai de bază transformare este ceea ce se numește bucle de derulare. Aceasta este o optimizare care vizează reducerea numărului de iterații ale unei bucle prin duplicarea corpului acesteia în mai multe exemplare. Este folosit pentru a reduce costul efectuării ramificației și a altor operațiuni de comparare a buclei. Pe procesoarele SIMD, acesta poate fi utilizat pentru a reduce timpul de execuție al buclelor care acționează asupra matricilor. De obicei, programatorii folosesc o buclă pentru a repeta procesarea tuturor elementelor dintr-o matrice, bucla procesând câte un element la un moment dat. Instrucțiunile noastre vectoriale permit procesarea mai multor elemente simultan, astfel încât mai multe runde de bucle pot fi combinate într-o singură instrucțiune SIMD. Dar acest lucru funcționează numai dacă elementele matricei sunt tratate independent sau într-un mod destul de simplu, caz în care bucla poate fi vectorizată. Pentru a face acest lucru, compilatorul va replica corpul buclei (instrucțiunile care trebuie repetate) în mai multe copii ale acestei bucle.

Exemplu, să luăm această buclă, scrisă în limbajul C:

int i; for (i = 0; i < 100; ++i) { a[i] = b[i] * 7 ; }

Acest lucru se poate face după cum urmează:

int i; for (i = 0; i < 100; i+=4) { a[i] = b[i] * 7 ; a[i+1] = b[i+1] * 7 ; a[i+2] = b[i+2] * 7 ; a[i+3] = b[i+3] * 7 ; }

Dacă compilatorul reproduce aceste instrucțiuni de câte ori o instrucțiune poate procesa elemente simultan, vectorizarea buclei devine banală. În exemplul nostru, dacă procesorul are vreodată o instrucțiune de multiplicare capabilă să proceseze 4 elemente ale matricei a sau b simultan, bucla derulată poate fi vectorizată destul de simplu.

Minereu pe benzi

Cu toate acestea, derularea buclelor nu este o optimizare valabilă pentru toate buclele. Să luăm exemplul buclei văzute mai sus. Dacă tabloul care trebuie manipulat are un număr de elemente care nu este multiplu de 4, bucla nu poate fi vectorizată, instrucțiunile de multiplicare putând procesa doar 4 elemente la un moment dat. Pentru a face acest lucru, compilatoarele folosesc de obicei două bucle: una care tratează elementele matricei cu instrucțiuni SIMD și alta care tratează elementele rămase cu instrucțiuni nonvector. Această transformare se numește extragere pe benzi .

De exemplu, dacă dorim să iterați pe o matrice de dimensiuni fixe care conține 102 elemente, bucla ar trebui descrisă astfel:

int i; for (i = 0; i < 100; i+=4) { a[i] = b[i] * 7 ; a[i+1] = b[i+1] * 7 ; a[i+2] = b[i+2] * 7 ; a[i+3] = b[i+3] * 7 ; } for (i = 100; i < 102; ++i) { a[i] = b[i] * 7 ; }

Dar procesoarele vectoriale conțin registre pentru a face mai ușoară gestionarea acestui tip de situație. În special, acestea conțin un registru de lungime vectorială , care stochează numărul de elemente pe care instrucțiunile noastre trebuie să le proceseze. Cu acest registru, este posibil să solicităm instrucțiunilor noastre vectoriale să proceseze doar primele n elemente ale unui vector: este suficient să plasăm valoarea n în acest registru. Evident, n trebuie să fie mai mic sau egal cu numărul maxim de elemente ale vectorului. Cu acest registru, nu este nevoie de o a doua buclă pentru a procesa elementele rămase și o instrucțiune vectorială simplă poate fi suficientă.

Conexiuni

Un alt obstacol în calea vectorizării: prezența ramurilor condiționate în buclele care urmează a fi vectorizate. Dacă o buclă conține ramuri condiționate, este posibil ca unele instrucțiuni să fie aplicate unor elemente și nu altora. Pentru a permite compilatorilor să deruleze aceste bucle de ramificare, procesoarele vectoriale încorporează tehnici în setul lor de instrucțiuni.

Se poate menționa în special Registrul de mască vectorială , care face posibilă implementarea predicției anumitor instrucțiuni vectoriale. Acest lucru face posibilă stocarea informațiilor care vor permite selectarea anumitor date și nu a altora pentru efectuarea calculului. Acest registru de mască vectorială va stoca un bit pentru fiecare element al vectorului care urmează să fie procesat. Dacă acest bit este la 1, instrucțiunea trebuie executată pe datele asociate cu acest bit. În caz contrar, instrucțiunea nu trebuie să o modifice. Este astfel posibil să se proceseze doar o parte a registrelor care stochează vectori SIMD.

Microarhitectură

Un procesor vectorial este alcătuit din mai multe elemente. La fel ca toate procesoarele, acesta conține în special registre, unități de calcul, un secvențial și alte circuite pentru accesarea memoriei. Fiecare procesor normal conține registre și unități de calcul care funcționează pe numere normale. Un procesor vectorial le are și ele.

Cu toate acestea, un procesor vectorial va avea circuite suplimentare. În special, sunt necesare registre de vectori, așa cum se vede mai sus. Dar un procesor vectorial are, de asemenea, una sau mai multe unități de calcul specializate în prelucrarea vectorilor. În plus, procesorul vectorial conține și un circuit responsabil pentru gestionarea schimburilor de date între memorie și registre de vectori: acest circuit este cel care gestionează instrucțiunile de acces la memorie.

Cachete

Procesoarele vectoriale pot avea cache. Memoriile cache de instrucțiuni sunt destul de frecvente. Pe de altă parte, cache-urile de date sunt adesea mai rare la acest tip de procesor. Provine din faptul că localitatea temporală a programelor care utilizează tabele este slabă? În plus, registrele vectoriale sunt adesea mai lungi decât liniile cache. În aceste condiții, trecerea printr-o memorie cache intermediară este inutilă: la fel de bine puteți trece direct prin registrele vectoriale. Astfel, procesoarele vectoriale au rareori cache și, dacă au, acestea sunt speciale (pot gestiona simultan un număr mare de cozi cache).

Mai mult decât atât, la procesoarele vectoriale care au memorii cache, aceste memorii cache sunt adesea utilizate numai pentru schimbul de date între memorie și registre non-vectoriale. Alte schimburi nu trec prin cache.

Acces la memorie

După cum s-a văzut mai sus, procesoarele vectoriale trebuie să încarce sau să salveze vectori complet în memorie. Prin urmare, procesoarele noastre au nevoie de o memorie care are o rată de biți destul de mare. Pentru aceasta, un procesor vectorial este adesea conectat la o memorie formată din mai multe bănci de memorie.

Fiecare dintre aceste bănci de memorie poate fi văzută ca un fel de sub-memorie. Fiecare dintre aceste bănci de memorie poate fi accesată în paralel cu celelalte. Astfel, o citire sau scriere vectorială poate fi împărțită în mai multe citiri / scrieri, distribuite pe mai multe bănci. Ceea ce este mai rapid decât accesarea unei singure memorii în serie.

Pentru ca această tehnică să funcționeze, adresele utilizate de programul nostru trebuie distribuite într-un anumit mod între diferitele bănci. Este necesar ca adresele apropiate unele de altele să fie distribuite în diferite bănci. În cel mai simplu caz, adresele consecutive vor corespunde băncilor consecutive. Astfel, dacă am N bănci, adresa A va fi plasată în banca 1, adresa A + 1 în banca 2 ... și adresa A + N în banca N. O memorie organizată așa numește o memorie „intercalată” . Aceste amintiri gestionează prost accesul la pas, astfel încât sunt deseori utilizate organizații mai complexe.

Unitate de calcul vector

Spre deosebire de procesoarele scalare , procesoarele vectoriale sunt special concepute și optimizate pentru a efectua aceeași instrucțiune pentru fiecare dintre datele conținute într-o matrice. Acestea sunt utilizate în principal pentru calcul intensiv pe un supercomputer .

Execuția unei operații de către unitatea de calcul este canalizată. Prin conducte, înțelegem că executarea fiecărei instrucțiuni va fi împărțită în mai multe etape, independente una de cealaltă. Seamănă puțin cu funcționarea unei linii de asamblare, în care tăiem producția unui obiect plin de sub-pași pe care îi desfășurăm unul după altul în diferite cutii.

În loc să așteptați executarea unei operațiuni pe o bucată de date pentru a fi finalizată înainte de a trece la următoarea, este astfel posibil să începeți procesarea unei noi bucăți de date fără a fi nevoie să așteptați ca cea veche să fie terminată. Acest lucru face posibilă executarea mai multor instrucțiuni simultan în unitatea noastră de calcul. Toate aceste instrucțiuni care sunt calculate sunt apoi în diferite etape.

Când o instrucțiune de calcul vectorial este efectuată de unitatea de calcul, aceasta din urmă își va executa operația pe fiecare element al vectorilor care urmează să fie prelucrați. Aceste elemente își vor începe execuția unul câte unul și vor vedea procesarea lor făcându-se pas cu pas.

Ora de pornire

Cu o unitate de calcul cu conductă, este posibil să efectuați un număr mare de operații simultane. Dacă o unitate de calcul vectorială este împărțită în N etape (N etape), atunci poate gestiona N operațiuni simultane, fiecare într-o etapă diferită.

Dar acest număr maxim de operații durează ceva timp pentru a fi atins. Elementele suficiente trebuie să fi fost încărcate în conductă. Toate etapele sunt utilizate cu N elemente încărcate în conductă. Fiecare dintre aceste elemente fiind încărcate în conductă unul câte unul, utilizarea optimă a conductei este atinsă numai atunci când unitatea de calcul începe să proceseze elementul N al vectorilor noștri.

Același fenomen se produce spre sfârșitul procesării vectorilor: nu mai au suficiente elemente pentru a umple diferitele etape ale conductei: atunci când există mai puține elemente de procesat decât sunt etape, utilizarea conductei este apoi suboptimă.

Înlănțuirea

Această tehnică a conductelor poate fi îmbunătățită și în unele cazuri speciale. Să ne imaginăm că avem trei vectori: A, B și C. Pentru fiecare element i al acestor vectori, să presupunem că vrem să efectuăm calculul A i + (B i * C i ). Deoarece procesorul nu are o instrucțiune care să permită efectuarea acestui calcul dintr-o dată, programatorul trebuie să utilizeze două instrucțiuni vectoriale: una pentru adunare și alta pentru multiplicare. S-ar putea crede că trebuie mai întâi să efectuăm înmulțirea pachetelor B și C, să stocăm rezultatul într-un pachet temporar și să adăugăm această matrice cu pachetul A.

Dar unele procesoare încorporează optimizări care permit ca conductele lor să fie utilizate mai eficient. Procesorul poate combina într-adevăr aceste două instrucțiuni independente și le poate trata intern ca și cum ar fi o singură instrucțiune. În loc să efectueze înmulțirea, apoi adunarea separat pentru fiecare element al vectorului, el poate efectua înmulțirea și adunarea pentru primul element, apoi continuă cu al doilea etc. Practic, fuzionează mai multe instrucțiuni vectoriale într-o singură instrucțiune vectorială care combină cele două. Acest principiu se numește Vector Chaining.

Într-un procesor vector care implementează Vector Chaining, cele două calcule combinate sunt efectuate unul după altul pentru fiecare element. Conducta unității de calcul trebuie să fie proiectată astfel încât rezultatul fiecărei etape a unui calcul să fie reutilizabil în următorul ciclu de ceas. Acest rezultat nu trebuie salvat într-un registru vector înainte de a putea fi reutilizat.

Mărci și modele

Aceste mărci produc sau au fabricat computere bazate pe sau care conțin procesoare vectoriale:

  • Cray de la Cray 1
  • Gama NEC SX
  • Fujitsu , VP400, VP2000, VPP500
  • Hitachi , S-820
  • IBM , opțiune vectorială (VF, pentru Vector Facility) a modelului 3090 numit 3090 / VF
  • RISC-V ( extensia V )
  • DEC , procesor vectorial opțional pe modelul 9000 numit 9000 / 440VP
  • CDC STAR 100 , ETA 10E, Cyber ​​205, Cyber ​​2000V
  • Texas Instruments TI-ASC (Computer științific avansat)

Unele console de jocuri sunt echipate cu procesoare vectoriale, cum ar fi procesorul vectorial Cell proiectat de IBM, Sony și Toshiba pentru PlayStation 3 .

Nu sunt procesoare vectoriale, ci SIMD-uri  :

  • AltiVec , SIMD de la IBM și Motorola , a fost utilizat pe diferite procesoare PowerPC , inclusiv gamele G4 și G5 utilizate în special pe computerele Apple în anii '90.
  • Diferitele extensii SIMD ale procesoarelor x86.
  • Motorul Emotion , proiectat de Sony și Toshiba pentru PlayStation 2 are două unități numite VPUs, dar care funcționează asemenea SIMDs.
  • NEON , SIMD-ul ARM.

Note și referințe

Articole similare