Care este dimensiunea memoriei cache? Ce afectează memoria cache a procesorului L1 L2 L3?

Toți utilizatorii cunosc bine elementele computerului, cum ar fi procesorul, care este responsabil pentru prelucrarea datelor, precum și memoria cu acces aleatoriu (RAM sau RAM), care este responsabilă pentru stocarea acestora. Dar probabil că nu toată lumea știe că există și o memorie cache a procesorului (Cache CPU), adică RAM-ul procesorului în sine (așa-numita ultra-RAM).

Care este motivul care i-a determinat pe designerii de calculatoare să folosească memorie dedicată pentru procesor? Nu este suficientă capacitatea RAM a computerului?

Într-adevăr, pentru o lungă perioadă de timp, computerele personale au făcut fără nicio memorie cache. Dar, după cum știți, procesorul este cel mai rapid dispozitiv de pe un computer personal și viteza lui a crescut cu fiecare nouă generație de CPU. În prezent, viteza sa este măsurată în miliarde de operații pe secundă. În același timp, RAM standard nu și-a crescut semnificativ performanța pe parcursul evoluției sale.

În general, există două tehnologii principale de cip de memorie - memoria statică și memoria dinamică. Fără să ne adâncim în detaliile designului lor, vom spune doar că memoria statică, spre deosebire de memoria dinamică, nu necesită regenerare; În plus, memoria statică folosește 4-8 tranzistori pentru un bit de informație, în timp ce memoria dinamică folosește 1-2 tranzistori. În consecință, memoria dinamică este mult mai ieftină decât memoria statică, dar în același timp mult mai lentă. În prezent, cipurile RAM sunt fabricate pe baza memoriei dinamice.

Evoluția aproximativă a raportului dintre viteza procesoarelor și RAM:

Astfel, dacă procesorul ar prelua informații din RAM tot timpul, ar trebui să aștepte o memorie dinamică lentă și ar fi inactiv tot timpul. În același caz, dacă s-ar folosi memoria statică ca RAM, costul computerului ar crește de câteva ori.

De aceea a fost dezvoltat un compromis rezonabil. Cea mai mare parte a memoriei RAM a rămas dinamică, în timp ce procesorul a primit propria memorie cache rapidă bazată pe cipuri de memorie statică. Volumul său este relativ mic - de exemplu, dimensiunea cache-ului de al doilea nivel este de doar câțiva megaocteți. Cu toate acestea, merită să ne amintim că întreaga memorie RAM a primelor computere IBM PC avea mai puțin de 1 MB.

În plus, oportunitatea introducerii tehnologiei de caching este influențată și de faptul că diferite aplicații situate în RAM încarcă diferit procesorul și, ca urmare, există o mulțime de date care necesită o prelucrare prioritară în comparație cu altele.

Istoricul memoriei cache

Strict vorbind, înainte ca memoria cache să fie mutată pe computerele personale, aceasta fusese deja folosită cu succes în supercomputere de câteva decenii.

Pentru prima dată, într-un PC bazat pe procesorul i80386 a apărut o memorie cache de doar 16 KB. Astăzi, procesoarele moderne folosesc diferite niveluri de cache, de la primul (cel mai rapid cache de cea mai mică dimensiune - de obicei 128 KB) până la al treilea (cel mai lent cache de cea mai mare dimensiune - până la zeci de MB).

La început, memoria cache externă a procesorului a fost localizată pe un cip separat. Cu timpul, însă, acest lucru a făcut ca magistrala situată între cache și procesor să devină un blocaj, încetinind schimbul de date. În microprocesoarele moderne, atât primul cât și cel de-al doilea nivel de memorie cache sunt situate în miezul procesorului însuși.

Multă vreme, procesoarele au avut doar două niveluri de cache, dar procesorul Intel Itanium a fost primul care a prezentat un cache de nivel al treilea, comun tuturor nucleelor ​​de procesor. Există și dezvoltări de procesoare cu un cache pe patru niveluri.

Arhitecturi și principii cache

Astăzi se cunosc două tipuri principale de organizare a memoriei cache, care provin din primele dezvoltări teoretice în domeniul ciberneticii - arhitecturile Princeton și Harvard. Arhitectura Princeton presupune un singur spațiu de memorie pentru stocarea datelor și comenzilor, în timp ce arhitectura Harvard implică unele separate. Majoritatea procesoarelor x86 de computere personale folosesc un tip separat de memorie cache. În plus, un al treilea tip de memorie cache a apărut și în procesoarele moderne - așa-numitul buffer de traducere asociativă, conceput pentru a accelera conversia adreselor de memorie virtuală ale sistemului de operare în adrese de memorie fizică.

O diagramă simplificată a interacțiunii dintre memoria cache și procesor poate fi descrisă după cum urmează. În primul rând, procesorul verifică prezența informațiilor necesare procesorului în cel mai rapid cache de primul nivel, apoi în cel de al doilea nivel, etc. Dacă informațiile necesare nu sunt găsite în niciun nivel de cache, atunci o numesc o eroare sau o pierdere de cache. Dacă nu există deloc informații în cache, atunci procesorul trebuie să o ia din RAM sau chiar din memoria externă (de pe hard disk).

Ordinea în care procesorul caută informații în memorie:

Acesta este modul în care Procesorul caută informații

Pentru a controla funcționarea memoriei cache și interacțiunea acesteia cu unitățile de calcul ale procesorului, precum și RAM, există un controler special.

Schema de organizare a interacțiunii nucleului procesorului, cache-ului și RAM:

Controlerul cache este legătura cheie între procesor, RAM și memoria cache

Trebuie remarcat faptul că stocarea în cache a datelor este un proces complex care utilizează multe tehnologii și algoritmi matematici. Printre conceptele de bază folosite în cache se numără metodele de scriere în cache și arhitectura asociativității cache.

Metode de scriere în cache

Există două metode principale de scriere a informațiilor în memoria cache:

  1. Metoda de rescriere – datele sunt scrise mai întâi în cache, iar apoi, când apar anumite condiții, în RAM.
  2. Metoda de scriere – datele sunt scrise simultan în RAM și cache.

Arhitectura asociativității cache

Arhitectura asociativității cache definește modul în care datele din RAM sunt mapate în cache. Principalele opțiuni pentru memorarea în cache a arhitecturii asociativității sunt:

  1. Cache mapat direct - o anumită secțiune a memoriei cache este responsabilă pentru o anumită secțiune a RAM
  2. Cache complet asociativ - orice parte a memoriei cache poate fi asociată cu orice parte a memoriei RAM
  3. Cache mixtă (asociativ de set)

Nivelurile de cache diferite pot utiliza de obicei arhitecturi de asociativitate cache diferite. Memorarea în cache RAM mapată directă este cea mai rapidă opțiune de stocare în cache, așa că această arhitectură este de obicei folosită pentru cache-urile mari. La rândul său, un cache complet asociativ are mai puține erori de cache (gașuri).

Concluzie

În acest articol, ați fost introdus în conceptul de memorie cache, arhitectura memoriei cache și metodele de stocare în cache și ați învățat cum afectează performanța unui computer modern. Prezența memoriei cache poate optimiza în mod semnificativ funcționarea procesorului, poate reduce timpul de inactivitate al acestuia și, în consecință, poate crește performanța întregului sistem.

Toate procesoarele de la sfârșitul anilor 90 au memorie cache internă (sau pur și simplu cache). Cache-ul este o memorie de mare viteză în care sunt transferate instrucțiunile și datele procesate direct de procesor.

Procesoarele moderne au memorie cache încorporată de două niveluri - primul (L1) și al doilea (L2). Procesorul este puțin mai rapid cu conținutul cache-ului L1, în timp ce cache-ul L2 este de obicei puțin mai mare. Memoria cache este accesată fără o stare de așteptare, de exemplu. Cache-ul de nivel 1 (cache pe cip) funcționează la viteza procesorului.

Aceasta înseamnă că, dacă datele de care are nevoie procesorul sunt în cache, nu există întârzieri de procesare. În caz contrar, procesorul trebuie să preia date din memoria principală, ceea ce reduce semnificativ performanța sistemului.

Pentru a înțelege calitativ principiul de funcționare a memoriei cache la ambele niveluri, să luăm în considerare o situație de zi cu zi ca exemplu.

Vii la o cafenea să iei prânzul în fiecare zi, la aceeași oră, și stai mereu la aceeași masă. Comandați întotdeauna un set standard de trei feluri.

Chelnerul alearga la bucatarie, bucatarul ii pune pe o tava si apoi iti aduc comanda. Și așa, să zicem, a treia zi, chelnerul, pentru a nu mai fi nevoit să alerge încă o dată la bucătărie, vă întâlnește la ora stabilită cu un prânz cald gata făcut pe o tavă.

Nu trebuie să așteptați comanda și să economisiți mult timp. Tava cu vasele tale este primul nivel cache. Dar în a patra zi vrei să adaugi brusc un alt fel de mâncare, să zicem desert.

Desi o tava cu comanda ta te astepta deja la ora stabilita, chelnerul tot trebuia sa alerge la bucatarie la desert.

Și pe a cincea - din nou un meniu de trei articole. Pe al șaselea - desert din nou, dar diferit de precedentul. Iar chelnerul, neștiind ce desert vrei să comanzi (și nici măcar neștiind dacă vei comanda ceva), decide să facă următorul pas: lângă masa ta pune un dulap cu mai multe tipuri de desert.

Și dacă exprimi o dorință, totul este la îndemână, nu este nevoie să alergi la bucătărie. Dulapul pentru desert este un cache de al doilea nivel.

Performanța procesorului depinde semnificativ de dimensiunea cache-ului L1 (de la 16 la 128 KB) și L2 (de la 64 KB la 512 KB, în Pentium III Heop și AMD Opteron până la 4 MB).

Procesoarele Intel Pentium III și procesoarele Celeron bazate pe acesta au o dimensiune cache L1 de 32 KB. Intel Pentium 4, precum și versiunile Celeron și Cheop bazate pe acesta, au doar 20 KB. Procesoarele AMD Duron, Athlon (inclusiv XP/MP) și Opteron, precum și VIA SZ, conțin 128 KB de cache L1.

Procesoarele moderne dual-core au un cache de prim nivel pentru fiecare nucleu separat, așa că uneori în descrierea cache-ului putem vedea numărul 128x2. Aceasta înseamnă că fiecare nucleu de procesor are 128 KB de cache L1.

Dimensiunea cache-ului L1 este importantă pentru obținerea unor performanțe ridicate în majoritatea sarcinilor obișnuite (aplicații de birou, jocuri, majoritatea aplicațiilor de server etc.). Eficacitatea sa este deosebit de puternică pentru calculul threaded (de exemplu, procesarea video).

Acesta este unul dintre motivele pentru care Pentium 4 este relativ ineficient pentru majoritatea aplicațiilor obișnuite (deși acest lucru este compensat de viteza mare de ceas). Cache-ul L1 funcționează întotdeauna (schimbă informații cu miezul procesorului) la frecvența internă a procesorului.

În schimb, memoria cache L2 în diferite modele de procesoare funcționează la frecvențe diferite (și, în consecință, performanță). Începând cu Intel Pentium II, multe procesoare au folosit un cache L2 care funcționează la jumătate din frecvența internă a procesorului.

Această soluție a fost folosită în procesoarele Intel Pentium III învechite (până la 550 MHz) și AMD Athlon învechite (în unele dintre ele memoria cache L2 internă funcționa la o treime din frecvența de bază a procesorului). Mărimea cache-ului L2 variază, de asemenea, între procesoare.

La procesoarele Intel Pentium III mai vechi și unele mai noi, dimensiunea cache-ului L2 este de 512 KB, la alte procesoare Pentium III este de 256 KB. Procesorul Intel Celeron bazat pe Pentium III era disponibil cu cache L2 de 128 și 256 KB, în timp ce procesorul bazat pe Pentium 4 era disponibil cu doar 128 KB. Diverse versiuni ale versiunii Xeon a Intel Pentium 4 au până la 4 MB de cache L2.

Noile procesoare Pentium 4 (unele serii cu o frecvență de 2000 MHz și toate pentru frecvențe mai mari) au 512 KB de cache L2, restul Pentium 4 au 256 KB. Procesoarele Xeop (bazate pe Pentium 4) au 256 sau 512 KB cache L2.

În plus, au și un cache L3 de nivel al treilea. Cache-ul L3 integrat, combinat cu o magistrală rapidă de sistem, formează un canal de schimb de date de mare viteză cu memoria sistemului.

De regulă, numai procesoarele pentru soluții de server sau modele speciale de procesoare „desktop” sunt echipate cu memorie cache L3. De exemplu, liniile de procesoare precum Xeon DP, Itanium 2 și Xeon MP au memorie cache L3.

Procesorul AMD Duron are 128 KB cache L1 și 64 KB cache L2. Procesoarele Athlon (cu excepția celui mai vechi), Athlon MP și majoritatea variantelor Athlon XP au 128 KB de cache L1 și 256 KB de cache L2, iar cel mai recent Athlon XP (2500+, 2800+, 3000+ și mai mare) au 512 KB de L2 cache. AMD Opteron conține 1 MB de cache L2.

Cele mai recente modele de procesoare Intel Pentium D, Intel Pentium M, Intel Core 2 Duo sunt disponibile cu 6 MB de cache L2 și Core 2 Quad - 12 MB de cache L2.

Cel mai recent procesor Intel Core i7 la momentul scrierii acestei cărți are 64 KB de memorie cache L1 pentru fiecare dintre cele 4 nuclee, precum și 256 KB de memorie L2 pentru fiecare nucleu. Pe lângă primul și al doilea nivel cache, procesorul are și un al treilea nivel cache comun tuturor nucleelor, egal cu 8 MB.

Pentru procesoarele care pot avea dimensiuni de cache L2 diferite (sau în cazul Intel Xeon MP - L3) pentru același model, această dimensiune trebuie să fie indicată în momentul vânzării (desigur, prețul procesorului depinde de el). Dacă procesorul este vândut într-un pachet „cutie” (livrare în cutie), dimensiunea memoriei cache este de obicei indicată pe acesta.

Pentru sarcinile normale ale utilizatorului (inclusiv jocuri), viteza cache-ului L2 este mai importantă decât dimensiunea acestuia; pentru sarcinile de server, dimpotrivă, volumul este mai important. Cele mai productive servere, în special cele cu cantități mari de RAM (câțiva gigaocteți), necesită dimensiunea maximă și viteza maximă a cache-ului L2.

Versiunile Cheop ale procesoarelor Pentium III rămân de neegalat la acești parametri. (Procesorul Xeon MP se dovedește în continuare a fi mai productiv în sarcinile serverului decât Pentium III Xeon, datorită frecvenței de ceas mai mare a procesorului în sine și a magistralei de memorie.) Din cele de mai sus, concluzionăm: memoria cache îmbunătățește interacțiunea dintre un procesor rapid și o memorie RAM mai lentă și, de asemenea, vă permite să minimizați perioadele de așteptare care apar în timpul procesării datelor. Cache-ul L2 situat pe cipul procesorului joacă un rol decisiv în acest sens.

Unul dintre factorii importanți care crește performanța procesorului este prezența memoriei cache, sau mai degrabă volumul acesteia, viteza de acces și distribuția între niveluri.

De ceva vreme, aproape toate procesoarele sunt echipate cu acest tip de memorie, ceea ce dovedește încă o dată utilitatea prezenței sale. În acest articol, vom vorbi despre structura, nivelurile și scopul practic al memoriei cache, ceea ce este foarte important. caracteristicile procesorului.

Ce este memoria cache și structura acesteia

Memoria cache este o memorie ultra-rapidă utilizată de procesor pentru a stoca temporar datele care sunt accesate cel mai frecvent. Așa putem descrie pe scurt acest tip de memorie.

Memoria cache este construită pe flip-flop, care, la rândul lor, sunt formate din tranzistori. Un grup de tranzistoare ocupă mult mai mult spațiu decât aceiași condensatori care alcătuiesc RAM. Acest lucru implică multe dificultăți în producție, precum și limitări de volum. De aceea, memoria cache este o memorie foarte scumpă, având în același timp volume neglijabile. Dar din această structură provine principalul avantaj al unei astfel de memorie - viteza. Deoarece bistabilele nu au nevoie de regenerare, iar timpul de întârziere al porții pe care sunt asamblate este mic, timpul de comutare a flip-flop-ului de la o stare la alta are loc foarte repede. Acest lucru permite memoriei cache să funcționeze la aceleași frecvențe ca și procesoarele moderne.

De asemenea, un factor important este plasarea memoriei cache. Este situat pe cipul procesorului propriu-zis, ceea ce reduce semnificativ timpul de acces. Anterior, memoria cache a unor niveluri era amplasată în afara cipului procesorului, pe un cip SRAM special undeva pe placa de bază. Acum, aproape toate procesoarele au memorie cache situată pe cipul procesorului.


Pentru ce se folosește memoria cache a procesorului?

După cum am menționat mai sus, scopul principal al memoriei cache este stocarea datelor care sunt utilizate frecvent de procesor. Cache-ul este un buffer în care sunt încărcate datele și, în ciuda dimensiunii sale mici (aproximativ 4-16 MB) procesoare moderne, oferă o creștere semnificativă a performanței în orice aplicație.

Pentru a înțelege mai bine nevoia memoriei cache, să ne imaginăm organizarea memoriei unui computer ca un birou. Memoria RAM va fi un cabinet cu foldere pe care contabilul le accesează periodic pentru a prelua blocuri mari de date (adică foldere). Și tabelul va fi o memorie cache.

Există elemente care sunt așezate pe biroul contabilului, la care se referă de mai multe ori pe parcursul unei ore. De exemplu, acestea ar putea fi numere de telefon, câteva exemple de documente. Aceste tipuri de informații sunt situate chiar pe masă, ceea ce, la rândul său, crește viteza de acces la ele.

În același mod, datele pot fi adăugate din acele blocuri mari de date (dosare) la tabel pentru o utilizare rapidă, de exemplu, un document. Când acest document nu mai este necesar, acesta este plasat înapoi în dulap (în RAM), ștergând astfel tabelul (memoria cache) și eliberând acest tabel pentru documente noi care vor fi utilizate în următoarea perioadă de timp.

De asemenea, cu memoria cache, dacă există date care este cel mai probabil să fie accesate din nou, atunci aceste date din RAM sunt încărcate în memoria cache. Foarte des, acest lucru se întâmplă prin co-încărcarea datelor care este cel mai probabil să fie utilizate după datele curente. Adică, există presupuneri despre ceea ce va fi folosit „după”. Acestea sunt principiile complexe de funcționare.

Nivelurile cache ale procesorului

Procesoarele moderne sunt echipate cu un cache, care constă adesea din 2 sau 3 nivele. Desigur, există și excepții, dar acesta este adesea cazul.

În general, pot exista următoarele niveluri: L1 (primul nivel), L2 (al doilea nivel), L3 (al treilea nivel). Acum puțin mai multe detalii despre fiecare dintre ele:

Cache de prim nivel (L1)– cel mai rapid nivel de memorie cache care funcționează direct cu nucleul procesorului, datorită acestei interacțiuni strânse, acest nivel are cel mai scurt timp de acces și funcționează la frecvențe apropiate procesorului. Este un buffer între procesor și memoria cache de al doilea nivel.

Vom lua în considerare volumele pe un procesor de înaltă performanță Intel Core i7-3770K. Acest procesor este echipat cu 4x32 KB cache L1 4 x 32 KB = 128 KB. (32 KB per nucleu)

Cache al doilea nivel (L2)– al doilea nivel este la scară mai mare decât primul, dar, ca urmare, are „caracteristici de viteză” mai mici. În consecință, servește ca un tampon între nivelurile L1 și L3. Dacă ne uităm din nou la exemplul nostru Core i7-3770 K, atunci dimensiunea memoriei cache L2 este 4x256 KB = 1 MB.

Cache de nivel 3 (L3)– al treilea nivel, din nou, este mai lent decât precedentele două. Dar este încă mult mai rapid decât RAM. Dimensiunea cache-ului L3 în i7-3770K este de 8 MB. Dacă cele două niveluri anterioare sunt partajate de fiecare nucleu, atunci acest nivel este comun întregului procesor. Indicatorul este destul de solid, dar nu exorbitant. Deoarece, de exemplu, pentru procesoarele din seria Extreme, cum ar fi i7-3960X, este de 15 MB, iar pentru unele procesoare Xeon noi, mai mult de 20.

Procesoarele computerelor au făcut salturi semnificative în dezvoltare în ultimii ani. Dimensiunea tranzistoarelor scade în fiecare an, iar productivitatea crește. În același timp, legea lui Moore devine irelevantă. În ceea ce privește performanța procesorului, ar trebui să țineți cont nu numai de numărul de tranzistori și de frecvență, ci și de dimensiunea memoriei cache.

Poate că ați auzit deja despre memoria cache când căutați informații despre procesoare. Dar, de obicei, nu acordăm prea multă atenție acestor numere; nici măcar nu ies prea mult în evidență în publicitatea procesoarelor. Să ne dăm seama ce afectează memoria cache a procesorului, ce tipuri de cache există și cum funcționează totul.

În termeni simpli, memoria cache a procesorului este pur și simplu o memorie foarte rapidă. După cum știți deja, un computer are mai multe tipuri de memorie. Aceasta este o memorie permanentă care este folosită pentru a stoca date, sistemul de operare și programe, cum ar fi un SSD sau un hard disk. Computerul folosește și RAM. Aceasta este memoria cu acces aleatoriu, care funcționează mult mai rapid în comparație cu memoria permanentă. În cele din urmă, procesorul are blocuri de memorie și mai rapide, care se numesc colectiv cache-uri.

Dacă te gândești la memoria unui computer ca la o ierarhie bazată pe viteza sa, memoria cache ar fi în vârful acelei ierarhii. În plus, este cel mai aproape de nucleele de calcul, deoarece face parte din procesor.

Memoria cache a procesorului este o memorie statică (SRAM) și este concepută pentru a accelera lucrul cu RAM. Spre deosebire de memoria dinamică cu acces aleatoriu (DRAM), aceasta poate stoca date fără a le actualiza constant.

Cum funcționează memoria cache a procesorului?

După cum probabil știți deja, un program este un set de instrucțiuni pe care procesorul le execută. Când rulați un program, computerul trebuie să transfere aceste instrucțiuni din memoria permanentă pe procesor. Aici intervine ierarhia memoriei. Mai întâi, datele sunt încărcate în RAM și apoi transferate pe procesor.

În zilele noastre, un procesor poate procesa un număr mare de instrucțiuni pe secundă. Pentru a profita la maximum de capacitățile sale, procesorul are nevoie de memorie super rapidă. De aceea a fost dezvoltat cache-ul.

Controlerul de memorie al procesorului face treaba de a prelua date din RAM și de a le trimite în cache. În funcție de procesorul utilizat în sistemul dvs., acest controler poate fi situat în podul de nord al plăcii de bază sau în procesorul însuși. Cache-ul stochează, de asemenea, rezultatele execuției instrucțiunilor în procesor. În plus, memoria cache a procesorului are și propria sa ierarhie.

Nivelurile cache ale procesorului - L1, L2 și L3

Memoria cache a procesorului este împărțită în trei niveluri: L1, L2 și L3. Această ierarhie se bazează și pe viteza memoriei cache, precum și pe dimensiunea acestuia.

  • Cache L1 (cache de primul nivel)- Acesta este cel mai rapid tip de cache din procesor. În ceea ce privește prioritatea de acces, acest cache conține datele de care programul ar putea avea nevoie pentru a executa o anumită instrucțiune;
  • Cache L2 (cache al doilea nivel al procesorului)- mai lent comparativ cu L1, dar mai mare ca dimensiune. Volumul său poate fi de la 256 kiloocteți până la opt megaocteți. Cache-ul L2 conține date de care procesorul ar putea avea nevoie în viitor. Cele mai multe procesoare moderne au cache L1 și L2 pe nucleele procesorului, fiecare nucleu având propriul cache;
  • Cache L3 (cache al treilea nivel)- acesta este cel mai mare și cel mai lent cache. Dimensiunea sa poate varia de la 4 la 50 de megaocteți. În procesoarele moderne, un spațiu separat este alocat pe cip pentru memoria cache L3.

În acest moment, acestea sunt toate nivelurile de cache al procesorului; Intel a încercat să creeze un cache L4, cu toate acestea, această tehnologie nu a luat rădăcini încă.

Pentru ce este memoria cache din procesor?

Este timpul să răspundem la întrebarea principală a acestui articol: ce afectează memoria cache a procesorului? Datele circulă din RAM în memoria cache L3, apoi în L2 și apoi în L1. Atunci când procesorul are nevoie de date pentru a efectua o operație, încearcă să le găsească în cache-ul L1 și dacă le găsește, atunci această situație se numește hit cache. În caz contrar, căutarea continuă în cache-urile L2 și L3. Dacă datele încă nu pot fi găsite, se face o solicitare către RAM.

Știm acum că memoria cache este concepută pentru a accelera transferul de informații între RAM și procesor. Timpul necesar pentru a prelua datele din memorie se numește latență. Cache-ul L1 are cea mai mică latență, deci este cea mai rapidă, cache-ul L3 are cea mai mare. Când nu există date în cache, avem o latență și mai mare, deoarece procesorul trebuie să acceseze memorie.

Anterior, în proiectarea procesoarelor, cache-urile L2 și L3 au fost mutate în afara procesorului, ceea ce a dus la latențe mari. Cu toate acestea, reducerea procesului de fabricație utilizat pentru fabricarea procesoarelor permite plasarea miliardelor de tranzistori într-un spațiu mult mai mic decât înainte. Ca rezultat, spațiul este eliberat pentru a plasa memoria cache cât mai aproape de nuclee, reducând și mai mult latența.

Cum afectează memoria cache performanța?

Efectul unui cache asupra performanței unui computer depinde direct de eficiența acestuia și de numărul de accesări în cache. Situațiile în care nu există date în cache reduc foarte mult performanța generală.

Imaginați-vă că procesorul încarcă date din memoria cache L1 de 100 de ori la rând. Dacă rata de accesare a memoriei cache este de 100%, procesorul va dura 100 de nanosecunde pentru a prelua aceste date. Cu toate acestea, de îndată ce rata de accesare scade la 99%, procesorul va trebui să preia date din memoria cache L2 și există deja o întârziere de 10 nanosecunde. Rezultatul este 99 nanosecunde pentru 99 de solicitări și 10 nanosecunde pentru 1 cerere. Prin urmare, reducerea procentului de accesare a memoriei cache cu 1% reduce performanța procesorului cu 10%.

În timp real, rata de accesare a memoriei cache este între 95 și 97%. Dar, după cum înțelegeți, diferența de performanță între acești indicatori nu este de 2%, ci de 14%. Rețineți că în exemplu presupunem că datele iertate sunt întotdeauna în cache-ul L2, în viața reală datele pot fi evacuate din cache, ceea ce înseamnă că va trebui să fie preluate din RAM, care are o latență de 80- 120 nanosecunde. Aici diferența dintre 95 și 97 la sută este și mai semnificativă.

Performanța slabă a memoriei cache în procesoarele AMD Bulldozer și Piledriver a fost unul dintre principalele motive pentru care au pierdut în fața procesoarelor Intel. În aceste procesoare, memoria cache L1 a fost împărțită între mai multe nuclee, ceea ce l-a făcut foarte ineficient. Procesoarele Ryzen moderne nu au această problemă.

Putem concluziona că cu cât dimensiunea memoriei cache este mai mare, cu atât performanța este mai mare, deoarece procesorul va putea obține datele de care are nevoie mai rapid în mai multe cazuri. Cu toate acestea, merită să acordați atenție nu numai dimensiunii cache-ului procesorului, ci și arhitecturii acestuia.

concluzii

Acum știți de ce este responsabilă memoria cache a procesorului și cum funcționează. Designul cache-ului evoluează constant, iar memoria devine din ce în ce mai rapidă și mai ieftină. AMD și Intel au efectuat deja multe experimente cu memoria cache, iar Intel chiar a încercat să folosească memoria cache L4. Piața procesoarelor crește mai rapid ca niciodată. Arhitectura cache va ține pasul cu puterea din ce în ce mai mare a procesoarelor.

În plus, se fac multe pentru a elimina blocajele pe care le au computerele moderne. Reducerea latenței memoriei este una dintre cele mai importante părți ale acestei lucrări. Viitorul pare foarte promițător.

Postări asemănatoare.

Aproape toți dezvoltatorii știu că memoria cache a procesorului este o memorie mică, dar rapidă, care stochează date din zonele de memorie vizitate recent - definiția este scurtă și destul de precisă. Cu toate acestea, cunoașterea detaliilor plictisitoare despre mecanismele de cache este necesară pentru a înțelege factorii care afectează performanța codului.

În acest articol vom analiza o serie de exemple care ilustrează diferite caracteristici ale cache-urilor și impactul acestora asupra performanței. Exemplele vor fi în C#; alegerea limbajului și a platformei nu afectează foarte mult evaluarea performanței și concluziile finale. Desigur, în limite rezonabile, dacă alegeți o limbă în care citirea unei valori dintr-o matrice este echivalentă cu accesarea unui tabel hash, nu veți obține niciun rezultat interpretabil. Notele traducătorului sunt cu caractere cursive.

Habracut - - -

Exemplul 1: Acces la memorie și performanță

Cu cât crezi că este mai rapid al doilea ciclu decât primul?
int arr = int nou;

// mai întâi
pentru (int i = 0; i< arr.Length; i++) arr[i] *= 3;

// al doilea
pentru (int i = 0; i< arr.Length; i += 16) arr[i] *= 3;


Prima buclă înmulțește toate valorile din matrice cu 3, a doua buclă înmulțește doar la fiecare șaisprezecea valoare. Al doilea ciclu se încheie doar 6% lucrează primul ciclu, dar pe mașinile moderne ambele cicluri sunt executate în timp aproximativ egal: 80 msȘi 78 ms respectiv (pe aparatul meu).

Soluția este simplă - acces la memorie. Viteza acestor bucle este determinată în primul rând de viteza subsistemului de memorie și nu de viteza de multiplicare a întregului. După cum vom vedea în exemplul următor, numărul de accesări la RAM este același atât în ​​primul cât și în cel de-al doilea caz.

Exemplul 2: Impactul liniilor cache

Să cercetăm mai profund și să încercăm alte valori de pas, nu doar 1 și 16:
pentru (int i = 0; i< arr.Length; i += K /* шаг */ ) arr[i] *= 3;

Iată timpii de rulare ai acestei bucle pentru diferite valori de pas K:

Vă rugăm să rețineți că, cu valori de trepte de la 1 la 16, timpul de funcționare rămâne practic neschimbat. Dar cu valori mai mari de 16, timpul de rulare scade cu aproximativ jumătate de fiecare dată când dublăm pasul. Acest lucru nu înseamnă că bucla începe cumva în mod magic să ruleze mai repede, doar că și numărul de iterații scade. Punctul cheie este același timp de funcționare cu valori de pas de la 1 la 16.

Motivul pentru aceasta este că procesoarele moderne nu accesează memoria câte un octet, ci mai degrabă în blocuri mici numite linii cache. De obicei, dimensiunea șirului este de 64 de octeți. Când citiți orice valoare din memorie, cel puțin o linie de cache intră în cache. Accesul ulterior la orice valoare din acest rând este foarte rapid.

Deoarece 16 valori int ocupă 64 de octeți, buclele cu pași de la 1 la 16 accesează același număr de linii cache, sau mai precis, toate liniile cache ale matricei. La pasul 32, accesul are loc la fiecare a doua linie, la pasul 64, la fiecare a patra.

Înțelegerea acestui lucru este foarte importantă pentru unele tehnici de optimizare. Numărul de accesări la acesta depinde de locația datelor în memorie. De exemplu, datele nealiniate pot necesita două accesări la memoria principală în loc de una. După cum am aflat mai sus, viteza de operare va fi de două ori mai mică.

Exemplul 3: dimensiunile cache de nivel 1 și 2 (L1 și L2)

Procesoarele moderne au de obicei două sau trei niveluri de cache, numite de obicei L1, L2 și L3. Pentru a afla dimensiunile cache-urilor la diferite niveluri, puteți utiliza utilitarul CoreInfo sau funcția Windows API GetLogicalProcessorInfo. Ambele metode oferă, de asemenea, informații despre dimensiunea liniei de cache pentru fiecare nivel.

Pe aparatul meu, CoreInfo raportează 32 KB cache de date L1, 32 KB cache de instrucțiuni L1 și 4 MB cache de date L2. Fiecare nucleu are propriile sale cache L1 personale, cache-urile L2 sunt partajate de fiecare pereche de nuclee:

Harta procesor logic către cache: *--- Cache de date 0, Nivel 1, 32 KB, Assoc 8, LineSize 64 *--- Cache de instrucțiuni 0, Nivel 1, 32 KB, Assoc 8, LineSize 64 -*-- Cache de date 1, Level 1, 32 KB, Assoc 8, LineSize 64 -*-- Cache de instrucțiuni 1, Level 1, 32 KB, Assoc 8, LineSize 64 **-- Unified Cache 0, Level 2, 4 MB, Assoc 16, LineSize 64 --*- Cache de date 2, Nivel 1, 32 KB, Assoc 8, LineSize 64 --*- Cache de instrucțiuni 2, Nivel 1, 32 KB, Assoc 8, LineSize 64 ---* Cache de date 3, Nivel 1, 32 KB, Assoc 8, LineSize 64 ---* Instruction Cache 3, Level 1, 32 KB, Assoc 8, LineSize 64 --** Unified Cache 1, Level 2, 4 MB, Assoc 16, LineSize 64
Să verificăm aceste informații experimental. Pentru a face acest lucru, să trecem prin matricea noastră, incrementând fiecare a 16-a valoare - o modalitate ușoară de a schimba datele din fiecare linie de cache. Când ajungem la sfârșit, ne întoarcem la început. Să verificăm diferitele dimensiuni ale matricei; ar trebui să vedem o scădere a performanței atunci când matricea nu se mai încadrează în cache-urile de diferite niveluri.

Codul este:

int pași = 64 * 1024 * 1024; // numărul de iterații
int lengthMod = arr.Length - 1; // dimensiunea matricei -- puterea a doi

pentru (int i = 0; i< steps; i++)
{
// x & lengthMod = x % arr.Length, deoarece puteri de doi
arr[(i * 16) & lengthMod]++;
}


Rezultatele testului:

Pe aparatul meu, există scăderi vizibile ale performanței după 32 KB și 4 MB - acestea sunt dimensiunile cache-urilor L1 și L2.

Exemplul 4: Paralelismul instrucțiunilor

Acum să ne uităm la altceva. În opinia dumneavoastră, care dintre aceste două bucle se va executa mai repede?
int pași = 256 * 1024 * 1024;
int a = int nou;

// mai întâi
pentru (int i = 0; i< steps; i++) { a++; a++; }

// al doilea
pentru (int i = 0; i< steps; i++) { a++; a++; }


Se pare că a doua buclă rulează aproape de două ori mai repede, cel puțin pe toate mașinile pe care le-am testat. De ce? Deoarece comenzile din bucle au dependențe diferite de date. Primele comenzi au următorul lanț de dependențe:

În al doilea ciclu dependențele sunt:

Părțile funcționale ale procesoarelor moderne sunt capabile să efectueze un anumit număr de anumite operații simultan, de obicei nu un număr foarte mare. De exemplu, este posibil accesul paralel la datele din memoria cache L1 la două adrese și este posibilă și executarea simultană a două instrucțiuni aritmetice simple. În primul ciclu, procesorul nu poate folosi aceste capacități, dar poate în al doilea.

Exemplul 5: Asociativitatea cache

Una dintre întrebările cheie la care trebuie să se răspundă atunci când se proiectează un cache este dacă datele dintr-o anumită regiune de memorie pot fi stocate în orice celule cache sau numai în unele dintre ele. Trei solutii posibile:
  1. Cache de cartografiere directă,Datele fiecărei linii cache din RAM sunt stocate într-o singură locație cache predefinită. Cel mai simplu mod de a calcula maparea este: row_index_in_memory % number_of_cache_cells. Două linii mapate la aceeași celulă nu pot fi în cache în același timp.
  2. Cache-ul asociativ parțial cu N intrări, fiecare linie poate fi stocată în N locații cache diferite. De exemplu, într-un cache cu 16 intrări, o linie poate fi stocată într-una dintre cele 16 celule care alcătuiesc grupul. De obicei, rândurile cu biți egali de indici mai puțin semnificativi împart un grup.
  3. Cache complet asociativ, orice linie poate fi stocată în orice locație cache. Soluția este echivalentă cu un tabel hash în comportamentul său.
Cache-urile mapate direct sunt predispuse la dispute, de exemplu, atunci când două rânduri concurează pentru aceeași celulă, se scot reciproc din cache, eficiența este foarte scăzută. Pe de altă parte, cache-urile complet asociative, deși lipsite de acest dezavantaj, sunt foarte complexe și costisitoare de implementat. Cache-urile parțial asociative reprezintă un compromis tipic între complexitatea și eficiența implementării.

De exemplu, pe computerul meu, memoria cache L2 de 4 MB este o cache asociată parțială cu 16 intrări. Întreaga RAM este împărțită în seturi de linii conform celor mai puțin semnificative biți ai indicilor lor, liniile din fiecare set concurează pentru un grup de 16 celule cache L2.

Deoarece memoria cache L2 are 65.536 de celule (4 * 2 20 / 64) și fiecare grup este format din 16 celule, avem un total de 4.096 de grupuri. Astfel, cei 12 biți inferiori ai indexului de rând determină grupului căruia îi aparține acest rând (2 12 = 4.096). Ca rezultat, rândurile cu adrese care sunt multipli de 262.144 (4.096 * 64) împart același grup de 16 celule și concurează pentru spațiul din acesta.

Pentru ca efectele asociativității să aibă efect, trebuie să accesăm constant un număr mare de rânduri din același grup, de exemplu, folosind următorul cod:

public static long UpdateEveryKthByte (byte arr, int K)
{
const int rep = 1024 * 1024; // numărul de iterații

Cronometru sw = Cronometru.StartNew();

int p = 0;
pentru (int i = 0; i< rep; i++)
{
arr[p]++;

P += K; dacă (p >= arr.Lungime) p = 0;
}

Sw.Stop();
return sw.ElapsedMilliseconds;
}


Metoda incrementează fiecare K-lea element al tabloului. Când ajungem la capăt, începem din nou. După un număr destul de mare de iterații (2 20), ne oprim. Am făcut rulări pentru diferite dimensiuni de matrice și valori ale pașilor K. Rezultate (albastru - timp de rulare lung, alb - scurt):

Zonele albastre corespund acelor cazuri în care, cu modificări constante ale datelor, memoria cache nu este capabilă să găzduiască toate datele necesare simultan. O culoare albastră strălucitoare indică un timp de funcționare de aproximativ 80 ms, aproape alb - 10 ms.

Să ne ocupăm de zonele albastre:

  1. De ce apar linii verticale? Liniile verticale corespund valorilor pasului la care sunt accesate prea multe rânduri (mai mult de 16) dintr-un grup. Pentru aceste valori, memoria cache cu 16 intrări a mașinii mele nu poate găzdui toate datele necesare.

    Unele dintre valorile stride proaste sunt puteri de doi: 256 și 512. De exemplu, luați în considerare stride 512 și o matrice de 8 MB. Cu acest pas, există 32 de secțiuni în matrice (8 * 2 20 / 262 144), care concurează între ele pentru celule din 512 grupuri de cache (262 144 / 512). Există 32 de secțiuni, dar există doar 16 celule în cache pentru fiecare grup, așa că nu există suficient spațiu pentru toată lumea.

    Alte valori ale pașilor care nu sunt puteri de doi sunt pur și simplu ghinioniste, ceea ce provoacă un număr mare de accesări la aceleași grupuri de cache și, de asemenea, duce la apariția unor linii albastre verticale în figură. În acest moment, iubitorii de teoria numerelor sunt invitați să gândească.

  2. De ce se rup liniile verticale la limita de 4 MB? Când dimensiunea matricei este de 4 MB sau mai puțin, memoria cache cu 16 intrări se comportă ca un cache complet asociativ, adică poate găzdui toate datele din matrice fără conflicte. Nu există mai mult de 16 zone care luptă pentru un grup de cache (262.144 * 16 = 4 * 2 20 = 4 MB).
  3. De ce există un triunghi albastru mare în stânga sus? Pentru că, cu un pas mic și o matrice mare, memoria cache nu este capabilă să încapă toate datele necesare. Gradul de asociativitate a cache-ului joacă un rol secundar aici; limitarea este legată de dimensiunea cache-ului L2.

    De exemplu, cu o dimensiune a matricei de 16 MB și un pas de 128, accesăm fiecare 128 de octeți, modificând astfel fiecare a doua linie de cache a matricei. Pentru a stoca fiecare a doua linie în cache, aveți nevoie de 8 MB de cache, dar pe aparatul meu am doar 4 MB.

    Chiar dacă memoria cache ar fi complet asociativă, nu ar permite stocarea a 8 MB de date în el. Rețineți că în exemplul deja discutat cu un pas de 512 și o dimensiune a matricei de 8 MB, avem nevoie doar de 1 MB de cache pentru a stoca toate datele necesare, dar acest lucru este imposibil din cauza asociativității insuficiente a cache-ului.

  4. De ce partea stângă a triunghiului câștigă treptat în intensitate? Intensitatea maximă apare la o valoare a pasului de 64 de octeți, care este egală cu dimensiunea liniei de cache. După cum am văzut în primul și al doilea exemplu, accesul secvenţial la același rând nu costă practic nimic. Să zicem, cu un pas de 16 octeți, avem patru accesări la memorie la prețul unuia.

    Deoarece numărul de iterații este același în testul nostru pentru orice valoare a pasului, un pas mai ieftin are ca rezultat un timp de rulare mai mic.

Efectele descoperite persistă la valori mari ale parametrilor:

Asociativitatea cache-ului este un lucru interesant care se poate manifesta în anumite condiții. Spre deosebire de celelalte probleme discutate în acest articol, nu este atât de gravă. Cu siguranță nu este ceva care necesită o atenție constantă atunci când scrieți programe.

Exemplul 6: Partiționarea cache falsă

Pe mașinile cu mai multe nuclee, este posibil să întâmpinați o altă problemă - coerența cache-ului. Miezurile procesorului au cache-uri parțial sau complet separate. Pe mașina mea, cache-urile L1 sunt separate (ca de obicei) și există, de asemenea, două cache-uri L2 partajate de fiecare pereche de nuclee. Detaliile pot varia, dar, în general, procesoarele moderne multi-core au cache-uri ierarhice pe mai multe niveluri. Mai mult, cele mai rapide, dar și cele mai mici cache aparțin nucleelor ​​individuale.

Când un nucleu modifică o valoare din memoria cache, alte nuclee nu mai pot folosi valoarea veche. Valoarea din cache-urile altor nuclee trebuie actualizată. Mai mult, trebuie actualizat întreaga linie cache, deoarece cache-urile operează pe date la nivel de rând.

Să demonstrăm această problemă cu următorul cod:

private static int s_counter = new int ;

private void UpdateCounter (poziție int)
{
pentru (int j = 0; j< 100000000; j++)
{
s_counter = s_counter + 3;
}
}


Dacă pe mașina mea cu patru nuclee apelez această metodă cu parametrii 0, 1, 2, 3 simultan din patru fire, atunci timpul de rulare va fi 4,3 secunde. Dar dacă apelez la metoda cu parametrii 16, 32, 48, 64, atunci timpul de rulare va fi doar 0,28 secunde.

De ce? În primul caz, este posibil ca toate cele patru valori procesate de fire de execuție la un moment dat să ajungă într-o singură linie de cache. De fiecare dată când un nucleu incrementează o valoare, acesta marchează celulele cache care conțin acea valoare în alte nuclee ca nevalide. După această operație, toate celelalte nuclee vor trebui să memoreze din nou linia în cache. Acest lucru face ca mecanismul de stocare să fie inoperabil, distrugând performanța.

Exemplul 7: Complexitatea hardware

Chiar și acum, când principiile funcționării cache-ului nu sunt un secret pentru tine, hardware-ul tot îți va oferi surprize. Procesoarele diferă unele de altele prin metode de optimizare, euristică și alte subtilități de implementare.

Cache-ul L1 al unor procesoare poate accesa două celule în paralel dacă aparțin unor grupuri diferite, dar dacă aparțin aceluiași grup, doar secvențial. Din câte știu eu, unii pot accesa chiar și diferite sferturi ale aceleiași celule în paralel.

Procesoarele vă pot surprinde cu optimizări inteligente. De exemplu, codul din exemplul anterior despre partajarea falsă a memoriei cache nu funcționează pe computerul meu de acasă așa cum a fost prevăzut - în cele mai simple cazuri, procesorul poate optimiza munca și poate reduce efectele negative. Dacă modifici puțin codul, totul cade la locul său.

Iată un alt exemplu de ciudatenii hardware ciudate:

private static int A, B, C, D, E, F, G;

private static void Ciudație ()
{
pentru (int i = 0; i< 200000000; i++)
{
<какой-то код>
}
}


Dacă în schimb<какой-то код>Înlocuiți trei opțiuni diferite, puteți obține următoarele rezultate:

Creșterea câmpurilor A, B, C, D durează mai mult decât creșterea câmpurilor A, C, E, G. Ce este și mai ciudat este că creșterea câmpurilor A și C durează mai mult decât câmpurile A, C Și E, G. Nu știu exact care sunt motivele pentru aceasta, dar poate sunt legate de băncile de memorie ( da, da, cu băncile obișnuite de memorie de economii de trei litri și nu ceea ce credeai). Dacă aveți vreo părere despre această chestiune, vă rugăm să vorbiți în comentarii.

Pe mașina mea, cele de mai sus nu sunt respectate, totuși, uneori există rezultate anormal de proaste - cel mai probabil, programatorul de sarcini își face propriile „ajustări”.

Lecția care trebuie învățată din acest exemplu este că este foarte dificil să preziceți complet comportamentul hardware-ului. Da, Poate sa preziceți multe, dar trebuie să vă confirmați constant predicțiile prin măsurători și teste.

Concluzie

Sper că tot ce am discutat mai sus v-a ajutat să înțelegeți structura cache-urilor procesorului. Acum puteți pune aceste cunoștințe în practică pentru a vă optimiza codul.