Sisteme multiprocesor cu memorie partajată
Cerințele impuse de procesoarele moderne la lățimea de bandă a memoriei pot fi reduse în mod semnificativ prin aplicarea cache-urilor mari pe mai multe niveluri. Apoi, dacă aceste cerințe sunt reduse, atunci mai multe procesoare vor putea să partajeze accesul în aceeași memorie. Din 1980, această idee, susținută de proliferarea microprocesoarelor, a stimulat numeroși dezvoltatori să creeze multiprocesoare mici, în care mai mulți procesatori împărtășesc o memorie fizică legată de ei printr-un autobuz partajat. Datorită dimensiunilor reduse ale procesoarelor și reducerii considerabile a lărgimii de bandă necesare a magistralei, realizate datorită posibilității de a implementa o memorie cache suficient de mare, aceste mașini au devenit extrem de rentabile. La prima dezvoltare a acestui tip de mașini a fost posibilă plasarea întregului procesor și a cache-ului pe o singură placă, care a fost apoi introdusă în panoul din spate, cu care a fost implementată arhitectura magistralei. Proiectele moderne permit plasarea a până la patru procesoare pe o singură placă. În Fig. 10.1 prezintă schema unei astfel de mașini.
Într-o astfel de mașină, cache-urile pot conține atât date partajate, cât și date private. Datele private sunt date care sunt utilizate de un procesor, în timp ce datele partajate sunt utilizate de mai mulți procesatori, oferind în principal un schimb între ei. Atunci când un element de date private este stocat în cache, valoarea sa este transferată în memoria cache pentru a reduce timpul de acces mediu, precum și lățimea de bandă necesară. Deoarece niciun alt procesor nu utilizează aceste date, acest proces este identic cu procesul pentru o mașină cu un singur procesor cu memorie cache. Dacă datele partajate sunt stocate în cache, atunci valoarea partajată este reprodusă și poate fi cuprinsă în mai multe cache-uri. Pe lângă reducerea latenței de acces și a lățimii de bandă necesare, această replicare a datelor contribuie, de asemenea, la o reducere generală a numărului de schimburi. Cu toate acestea, stocarea datelor partajate provoacă o nouă problemă: coerența cache-ului.
Coerență cache multiprocesor
Problema în cauză rezultă din faptul că valoarea elementului de date din memorie, stocată în două procesoare diferite, este disponibilă acestor procesoare numai prin cache-urile lor individuale. În Fig. 10.3 prezintă un exemplu simplu care ilustrează această problemă.
Problema de coerență a memoriei pentru multiprocesoare și dispozitive de I / O are multe aspecte. De obicei, microprocesoarele mici utilizează un mecanism hardware, numit un protocol, pentru a rezolva această problemă. Aceste protocoale se numesc protocoale de coerență în cache. Există două clase de astfel de protocoale:
- Protocoale bazate pe protocoale. Informațiile despre starea blocului de memorie fizică sunt conținute doar într-un singur loc, numit un director (fizic, directorul poate fi distribuit între nodurile sistemului). Această abordare va fi luată în considerare în Sec. 10.3.
- Protocoale de observare (snooping). Fiecare cache care conține o copie a datelor unui anumit bloc de memorie fizică are, de asemenea, o copie corespunzătoare a informațiilor despre serviciu despre starea sa. Nu există un sistem centralizat de înregistrare. De obicei, cache-urile sunt localizate pe un autobuz partajat, iar toți controlorii cache-ului urmăresc autobuzul (vizualizează-l) pentru a determina dacă conțin o copie a blocului corespunzător.
Fig. 10.3. Ilustrația problemei de coerență a memoriei cache
În sistemele multiprocesor, folosind microprocesoare cu memoria cache conectat la memoria partajată centralizată, protocoalele de observare au devenit populare ca să sondeze starea cache-uri, acestea pot utiliza o conexiune preexistent fizică - un autobuz de memorie.
Informal, problema de memorie este necesitatea coerentei de a se asigura că orice element de date citit returnează valoarea ultima dată când este scris pentru ea. Această definiție nu este în întregime corectă, deoarece este imposibil să se ceară ca operațiunea de citire a văzut imediat valoarea înregistrată în acest element de date într-un alt procesor. Dacă, de exemplu, o singură operație procesor de scriere precede operația de citire, din aceeași celulă pe un alt procesor într-un interval de timp foarte scurt, este imposibil să se garanteze faptul că valoarea de date de returnare de citire înregistrate, deoarece în acest moment, datele de înregistrare nu poate lăsa chiar procesorul. Problema exact când valoarea înregistrată trebuie să fie disponibilă pentru procesorul care efectuează citirea, este determinată de modelul selectat status coerent (consistent) memoriei și asociate cu implementarea sincronizării de calcul paralel. Prin urmare, în scopul de simplitate, presupunem că avem nevoie doar pentru a înregistra valoarea înregistrării tranzacției a fost de operații de citire disponibile care au avut loc puțin mai târziu că înregistrarea și procesarea operațiunii de scriere sunt întotdeauna vizibile în ordinea de execuție a acestora.
Cu această definiție simplă a unei stări consistente de memorie, putem garanta coerența prin oferirea a două proprietăți:
- Funcția de citire a unei celule de memorie de către un procesor care urmează operației de scriere în aceeași locație de memorie de către un alt procesor va obține valoarea înregistrată dacă operațiile de citire și scriere sunt suficient de separate una de cealaltă în timp.
- scrie operațiuni în aceeași celulă de memorie sunt realizate strict secvențial (uneori, spun că sunt serializate): aceasta înseamnă că două scrieri consecutive la aceeași locație de memorie vor fi respectate de către alte procesoare exact în ordinea în care acestea apar în programul procesorului care efectuează aceste operații de scriere.
Prima proprietate este în mod evident legată de definirea unei stări de memorie coerente (consecventă): dacă procesorul va citi mereu numai valoarea datelor vechi, am spune că memoria este incoerentă.
Necesitatea operațiilor de înregistrare strict secvențială este mai subtilă, dar și foarte importantă. Imaginați-vă că nu se respectă executarea strict consecventă a operațiilor de scriere. Apoi, procesorul P1 poate scrie date către celulă, apoi procesorul P2 scrie în această celulă. Executarea strict secvențială a operațiilor de scriere garantează două consecințe importante pentru această secvență de operații de scriere. În primul rând, se asigură că fiecare procesor din aparat va observa la un moment dat înregistrarea efectuată de procesorul P2. Dacă secvența operațiilor de scriere nu este respectată, atunci poate apărea o situație când un procesor va observa mai întâi operația de scriere a procesorului P2 și apoi va scrie operația procesorului P1 și va stoca această valoare înregistrată P1 pe o perioadă nedeterminată. O problemă mai subtilă apare cu menținerea unui model rezonabil de ordine de execuție a programului și coerență a memoriei pentru utilizator: imaginați-vă că cel de-al treilea procesor citește în mod constant aceeași celulă de memorie în care scriu procesoarele P1 și P2; trebuie să observe mai întâi valoarea înregistrată de P1 și apoi valoarea înregistrată de P2. Poate că nu va putea niciodată să vadă valoarea înregistrată de P1, deoarece înregistrarea de la P2 a apărut înainte de citire. Dacă chiar vede valoarea înregistrată de P1, ar trebui să vadă valoarea scrisă de P2 în lectură ulterioară. În mod similar, orice alt procesor care poate monitoriza valorile scrise de ambele P1 și P2 trebuie să respecte același comportament. Cea mai simplă modalitate de a obține astfel de proprietăți este aceea de a respecta cu strictețe ordinea operațiilor de scriere, astfel încât toate înregistrările din aceeași celulă să poată fi observate în aceeași ordine. Această proprietate se numește execuție secvențială (serializare) a operațiilor de scriere (scrierea serializării). Întrebarea când un procesor ar trebui să vadă valoarea scrisă de un alt procesor este destul de complexă și are un efect vizibil asupra performanței, în special în mașinile mari.
O alternativă la protocolul de returnare este să actualizați toate copiile elementului de date atunci când scrieți la acest element de date. Acest tip de protocol se numește un protocol de actualizare de scriere sau un protocol de difuzare de scriere. De obicei, în acest protocol, pentru a reduce cerințele privind lățimea de bandă, este util să monitorizăm dacă cuvântul din cache este un obiect partajat sau nu, și anume dacă acesta este conținut în alte cache-uri. Dacă nu, nu este nevoie să actualizați o altă memorie cache sau să transmiteți date actualizate către aceasta.
Diferența de performanță dintre protocoalele de înregistrare cu actualizare și cu anularea este determinată de trei caracteristici:
- Mai multe secvențiale scriu la același cuvânt, care nu sunt intercalate cu operațiuni de citire, necesită multiple operațiuni de traducere atunci când se utilizează protocolul de scriere a actualizării, ci doar o singură operație inițială de anulare când se utilizează protocolul de returnare.
- În prezența blocurilor lungi în memoria cache fiecare cuvânt scris la unitatea de memorie cache, necesară atunci când se utilizează un protocol de difuzare a scrie actualizarea, în timp ce doar prima intrare la orice cuvânt în bloc trebuie să genereze operații de anulare, prin înregistrarea de protocol la anularea. înregistrarea protocolul de anulare funcționează la nivelul unităților de memorie cache, în timp ce protocolul de înregistrare de actualizare trebuie să funcționeze la nivelul cuvintelor individuale (sau bytes, dacă înregistrați un octet).
- Întârzierea între scris cuvintele într-un singur procesor și citirea valorilor înregistrate ale celuilalt procesor este de obicei mai mică decât atunci când se utilizează înregistrarea cu schema de upgrade, deoarece datele înregistrate se traduce imediat într-un procesor care efectuează citirea (se presupune că acest procesor are o copie a datelor). Pentru comparație, folosind jurnalul înregistrează anularea într-un procesor, efectuează o citire, prima anulare se produce copii, atunci citirea se va face de date și suspendarea acestuia, atâta timp cât copia actualizată a blocului nu va fi disponibil și nu va reveni la procesor.
În plus față de anularea sau actualizarea copiilor corespunzătoare din blocul de cache în care a fost creată memoria cache, trebuie să plasăm și elementul de date în cazul în care apare o pierdere de memorie cache în timpul scriiturii. Memoria cache-scriere prin ultima valoare a elementului de date este ușor de găsit, deoarece toate datele înregistrate sunt trimise întotdeauna, de asemenea, în memoria din care poate fi selectată ultima valoare înregistrată a elementului de date (prezența tampoane de scriere poate duce la o anumită complexitate).
Pentru a implementa procesul de monitorizare, se pot utiliza etichetele cache obișnuite. În plus, bitul de valabilitate menționat mai devine ușor de implementat anularea. Lipsesc citirile cauzate de anulare sau de un alt eveniment nu sunt, de asemenea, greu de înțeles, deoarece se bazează pur și simplu pe posibilitatea de observație. Pentru operațiunile de scriere, ne-ar dori să știu dacă au existat alte copii în cache ale blocului, ca și în absența unor astfel de copii, înregistrarea nu poate fi trimis la autobuz, ceea ce reduce timpul pentru a efectua înregistrarea, precum și lățimea de bandă necesară.
Pentru a vedea dacă blocul este partajat, putem introduce un bit comun suplimentar asociat fiecărui bloc, la fel cum a fost făcut pentru biții valabili și pentru biții modificați sau murdari ai blocului. Prin adăugarea unui bit de stare care determină dacă blocul este partajat, putem decide dacă înregistrarea ar trebui să genereze o operație de anulare în protocolul cu anulare sau operația de traducere atunci când se utilizează protocolul de actualizare. Dacă o scriere are loc într-un bloc care este în starea "partajată" atunci când se utilizează protocolul de returnare, cache-ul generează o operație de anulare pe magistrală și marchează blocul drept privat. Nicio prelucrare suplimentară a anulării acestui bloc nu va mai fi trimisă acestui procesor. Un procesor cu o copie exclusivă a unui bloc de cache este denumit de obicei "proprietarul" blocului de cache.
Dacă utilizați protocolul de scriere cu actualizarea, dacă blocul este în starea "partajată", atunci fiecare înregistrare din acest bloc ar trebui să fie difuzată. În cazul unui protocol de anulare, atunci când operația de anulare este trimisă, starea blocului se schimbă de la "partajat" la "neseparat" (sau "privat"). Mai târziu, dacă un alt procesor cere acest bloc, statul ar trebui să se transforme din nou în "partajat". Deoarece cache-ul nostru de observare vede și toate lipsurile, știe când acest bloc de cache este solicitat de un alt procesor, iar starea sa ar trebui să devină "partajată".
Strategia de scriere a memoriei
Actualizați memoria în timpul difuzării
Fig. 10.4. Exemple de protocoale de monitorizare
Dacă procesorul utilizează un cache pe mai multe niveluri cu proprietăți de acoperire, atunci fiecare rând din cache-ul principal se află și în memoria cache secundară. Astfel, activitatea de monitorizare poate fi asociată cu o cache de nivel secundar, în timp ce majoritatea activităților procesorului pot fi asociate cu memoria cache primară. Dacă mecanismul de monitorizare este lovit în memoria cache secundară, atunci trebuie să efectueze arbitraj pentru cache-ul primar pentru a actualiza starea și a găsi eventual datele, ceea ce va duce de obicei la suspendarea procesorului. Această decizie a fost luată în multe sisteme moderne, deoarece un cache pe mai multe niveluri permite reducerea semnificativă a cerințelor privind lățimea de bandă. Uneori poate fi chiar util să copiați etichete în cache-ul secundar pentru a reduce în continuare numărul conflictelor dintre activitățile procesorului și mecanismul de monitorizare.
În sistemele reale, există mai multe variante de scheme de cache coerentei, oricare dintre acestea se utilizează dacă sistemul se bazează pe revocarea sau renovare, fie pentru a construi cache-ul pe principiile prin intermediul sau scrie-back, atunci când există o actualizare și „posesia“ dacă există un stat și ca este realizat. În Fig. Figura 10.4 prezintă mai multe protocoale de monitorizare și unele mașini care utilizează aceste protocoale.