![]() | ![]() |
|
|
La memoria centrale e i "colli di bottiglia" - Cache memory: come e perché
Nuove soluzioni tecniche come cache sincrona o pipelined burst, EDO RAM, SDRAM, WRAM, integrazione on-chip della cache di secondo livello e così via riportano alla ribalta un argomento, quello dei timing delle memorie, che su BETA non ha ancora trovato una trattazione organica.
Converrà introdurre l'argomento ricordando il ruolo delle memorie in un moderno PC.
Il compito della CPU consiste nell'eseguire continuamente, alla
massima velocità possibile, istruzioni che manipolano dati.
Sia le istruzioni, sia i dati su cui queste operano, sono contenuti nella
memoria centrale del computer e vengono prelevati dalla CPU man mano che
l'elaborazione va avanti. E' opportuno sottolineare che la CPU non è in grado
di eseguire istruzioni o manipolare dati che non siano presenti nella memoria
centrale: non può , ad esempio, prelevarli direttamente da memorie di massa o
da ROM.
Una CPU moderna viaggia a frequenze molto elevate, tipicamente tra i 100 e i
200 Mhz (= milioni di cicli di clock al secondo); inoltre, è in grado di
eseguire le istruzioni più comuni in pochi cicli di clock, spesso addirittura
in un solo ciclo.
Risulta evidente, allora, come lo scambio di dati con la memoria centrale
assumerà un ruolo fondamentale nelle prestazioni di un sistema: se non si ha
una buona efficienza nei trasferimenti di dati ed istruzioni da e verso la CPU,
anche la più veloce di esse non potrà che fornire prestazioni insoddisfacenti,
dovendo continuamente attendere che i dati giungano a destinazione.
Le CPU per PC usate oggi hanno un'architettura interna a 32 bit e un bus dati
di 64 bit, quindi di per sè potrebbero tranquillamente sostenere throughput di
dati molto elevati (nonostante un accesso alla memoria richieda comunque un minimo
di 2 cicli di clock); ma costruire memorie centrali in grado di "star
dietro" ad un simile traffico costerebbe moltissimo, risultando per di più
incompatibile con le concorrenti esigenze di compattezza e dissipazione del
calore (ricordiamo che sul mercato sono disponibili da te 252b17c mpo schedine SIMM in
grado di contenere 32 MB di memoria, il tutto su un'area di pochi centimetri
quadrati).
L'esigenze di mediare tra costi e prestazioni ha portato, dalla seconda metà degli anni '80 (in coincidenza con la comparsa dei processori Intel 80386), ad adottare architetture di sistema con impiego di cache memory.
Il principio è quello di interporre, tra la CPU e la memoria
centrale, una piccola quantità di memoria ad alta velocità , in grado di
"reggere il passo" della CPU e quindi agire da tampone, o se
preferite da serbatoio di transito, nei confronti della lenta memoria centrale.
Il concetto di cache non è stato certo inventato allora, dal momento che già
alla fine degli anni '70 veniva sfruttato nei sistemi di memoria virtuale dei
grossi elaboratori (v. il sistema di paginazione della memoria in MULTICS); tuttavia
per la prima volta veniva applicato su larga scala al problema specifico del
collo di bottiglia tra CPU e RAM, e con ottimi risultati, al punto che oggi una
certa quantità di cache memory viene direttamente integrata in tutte le CPU.
Il principio su cui si basa il successo della cache è noto come principio
di localizzazione: la CPU tende, in media, a prelevare istruzioni e
dati da locazioni di memoria abbastanza vicine tra loro, a causa della
struttura stessi dei programmi e dei metodi di allocazione della memoria usati
dai compilatori: questo principio fa sì che, per un certo periodo di tempo, la
zona di memoria interessata dal lavoro della CPU (working set) sia abbastanza limitata, al punto da poter essere
"copiata" in una piccola quantità di memoria molto veloce, appunto la
cache memory (sullo stesso principio si basano i sistemi di caching delle
memorie di massa, che siano software come SMARTDRIVE o hardware come le memorie
installate a bordo delle unità stesse e dei "controller intelligenti").
Per un certo periodo di tempo, dunque, la CPU potrà prelevare i dati di cui ha
bisogno direttamente dalla cache memory, senza chiamare in causa la memoria
centrale e quindi senza lunghe attese. Un principio analogo (anche se con
maggiore approssimazione) vale per le scritture (movimento di dati dalla CPU
alla memoria).
Esistono diversi metodi di caching, e quindi diverse architetture di cache memory: le distinzioni principali si fanno per metodo di mappatura della memoria (tramite il quale vengono individuate le categorie: cache a mappa diretta, cache associative e cache associative ad insiemi) e strategia di scrittura (abbiamo dunque le cache write-through e write-back). Le considerazioni che faremo valgono, comunque, in generale.
Per poter funzionare, una cache memory ha la necessità di essere riempita.
Il procedimento adottato è il seguente: ogni volta che la CPU ha bisogno di un
dato, se esso non è presente nella cache (cache miss) ci viene inserito,
leggendolo dalla memoria centrale: la prossima volta che la CPU chiederà quel
dato, potrà leggerlo direttamente dalla cache memory.
Per una serie di motivi architetturali (vedi i rimandi sui vari tipi di cache),
non è però pratico inserire nella cache un dato (double word, ovvero 32 bit)
alla volta: si sceglie allora di aggiornare un'intera linea.
Le dimensioni di una linea (o slot) dipendono da fattori quali le
dimensioni della cache; comunque in generale si usa, per gli attuali PC
386/486, una linea da 16 byte (4 double word), che salgono a 32 (4 quad word,
da 64 bit) per Pentium e Pentium Pro.
Ricapitolando, dunque, ogni volta che avviene un cache miss
occorre leggere dalla memoria centrale un'intera linea (line fill).
In linea di massima, ciò suona poco efficiente: considerando che una lettura da
memoria centrale può richiedere diversi cicli di clock, 4 letture per cache
miss potrebbero costituire un pedaggio un po' troppo alto, nonostante l'elevata
percentuale di successo (hit rate) assicurata generalmente dai sistemi di
cache.
Per ridurre questo problema prestazionale, viene utilizzata una
tecnica denominata burst read.
Considerando la struttura in cui è organizzata la memoria cache (mappatura), e
il principio di localizzazione prima ricordato, appare chiaro come ogni cache
line sia costituita da 4 "parole" consecutive, ovvero
occupanti, nella memoria centrale, posizioni adiacenti.
Ciò significa che, una volta conosciuta la posizione di una parola della linea,
quella delle altre 3 è facilmente ricavibile: non c'è alcun bisogno di
richiedere esplicitamente tutti e 4 gli indirizzi!
Questo permette di risparmiare tempo: una volta richiesto il primo indirizzo,
il chip che sovrintende al funzionamento della cache (la stessa Memory Unit
della CPU se la cache è integrata, uno degli integrati che costituiscono il
chipset della mother board se parliamo di cache esterna) preleva
automaticamente dalla memoria le altre 3 parole e l'intera operazione impiega
un minor numero di cicli di clock.
Più in dettaglio: la prima delle 4 parole viene letta in modo convenzionale, per cui richiederà un certo
numero M di cicli di clock; le 3 letture successive non richiederanno la
generazione ex-novo dell'indirizzo, per cui richiederanno ciascuna un numero N
di cicli di clock, con N <= M-1 (il numero di cicli risparmiati dipende da
un certo numero di fattori, anzitutto la velocità e l'organizzazione logica
della memoria centrale).
Il timing
di un burst read sarà quindi del tipo M-N-N-N,
e corrisponderà ad un'operazione di line fill: ecco quindi spiegato il
significato di opzioni del Chipset Setup
del tipo:
Torneremo comunque sull'argomento dei timing in un successivo articolo sui vari tipi di memoria disponibili.
Il meccanismo sopra descritto era riferito ad accessi tra CPU e memoria centrale, con interposto un solo stadio di cache (cache di primo livello); tuttavia, l'estensione al caso di cache a più livelli è immediata.
Supponiamo infatti di avere 2 livelli di cache, ad esempio una cache integrata nella CPU (primo livello, "1L") ed una esterna, sulla mother board (secondo livello, "2L"); se avviene un cache miss sulla cache di primo livello, abbiamo due possibilità : il dato può essere presente nella cache di secondo livello (che generalmente è di dimensioni molto maggiori), ed allora ci sarà un line fill sulla cache 1L (operazione particolarmente veloce, dato che stiamo comunque leggendo da una cache memory, la 2L); oppure il dato può mancare anche dalla cache 2L (avremo 2 cache miss) e dovremo effettuare, leggendo dalla memoria centrale, un line fill per entrambe le cache.
Per la scrittura il meccanismo di base è praticamente lo stesso.
Quando la CPU scrive un dato, se questo è contenuto nella cache va modificato:
per i motivi sopra esposti, l'intera cache line viene considerata "non più
valida" (sporca), nel senso che non corrisponde più al contenuto della
memoria centrale. Se la cache è di tipo write-through, il problema non sussiste in
quanto il dato verrà scritto immediatamente anche nella memoria centrale,
quindi la cache line non rimane mai "sporca"; tuttavia, nelle architetture
write-back si dovrà , ad un certo punto,
scrivere in memoria l'intera cache line.
Poiché anche le scritture sono operazioni piuttosto lunghe quando sono
effettuate nella RAM, è stato previsto anche qui un meccanismo
"burst": poiché le locazioni interessate alla scrittura sono
consecutive, solo la prima parola necessiterà del processo completo, mentre le
3 successive verranno scritte in modo più rapido; quindi troviamo ancora un
timing di tipo M-N-N-N. Va notato che i timing di burst read non corrispondono
necessariamente con quelli di burst write, essendo in generale diversi i tempi
di accesso in lettura da quelli in scrittura, specie in memoria centrale (le
scritture sono, in genere, leggermente più veloci grazie alla presenza di
buffer).
Naturalmente, anche per le scritture il discorso fatto per 1 livello di cache si puograve estendere facilmente al caso di più livelli, anche di tipo diverso (esempio: cache 1L write-through, cache 2L write-back).
A questo punto, è opportuno gettare uno sguardo alla storia della memoria cache su PC:
Con la comparsa del processore Intel 80386 (seconda metà anni
'80), appaiono "timidamente" i primi sistemi di cache su alcune
mother board: l'adozione della cache memory è consigliata dalla stessa Intel,
che integra nella documentazione del nuovo processore alcuni schemi di caching,
basati su un RAM-controller di propria produzione.
Le prime schede avevano 8 KB di cache memory da 25 nanosecondi, organizzata a
mappa diretta e di tipo write-through. Verso la fine dell'era 386, però , erano
diffuse schede con 128 KB di cache memory da 15 ns, in write-back.
Durante la progettazione dell'Intel 80486, divennero evidenti i
limiti architetturali imposti dalla compatibilità x86, in particolare il
ridotto numero di registri interni a disposizione che comporta la necessità di
frequenti accessi alla memoria. Per motivi prestazionali si decise, quindi, di
integrare nella CPU 8KB di cache memory: la prima volta nella storia
dell'hardware x86.
La cache integrata era del tipo associativo a 4 insiemi ed era in grado di
funzionare alla stessa velocità di un registro interno; proprio alla sua
presenza era dovuta buona parte dell'incremento prestazionale a parità di
frequenza rispetto al 386.
Poco dopo la comparsa dei 486 Intel, la Cyrix (alleata con IBM, Texas Instruments e SGS-Thompson) mise sul mercato delle CPU (486 DLC, SLC, DRX, DRX2, SLC2, Blue Lightning, ecc.) in grado di migliorare le prestazioni delle schede 386, semplicemente sostituendo la CPU 386 (compatibilità a livello di piedinatura); anche qui, la presenza di una cache memory integrata (pur se ridotta ad 1 KB, associativa a 2 insiemi) permetteva un apprezzabile incremento prestazionale, nonostante la logica di controllo integrata troppo primitiva entrasse in crisi con i trasferimenti DMA (per evitare problemi di coerenza, la cache interna andava "svuotata" ad ogni traferimento DMA), con ripercussioni piuttosto negative sulle prestazioni del sistema. Inoltre, molte delle schede madri 386 non avevano logica di supporto per la cache interna, che andava quindi attivata con un programmino "ad hoc".
La famiglia 80486 (considerando anche i vari "cloni") è stata piuttosto longeva: negli ultimi periodi si è assistito alla nascita di versioni con moltiplicatore interno di frequenza (da x2 dei primi modelli a x4 degli ultimi AMD 5x86, venduti come plug-in replacement per 80486 ma vicini al Pentium come architettura interna) e con cache integrata più "importante": fino a 16 KB in write-back sugli ultimi modelli AMD e Cyrix, che sfoderano prestazioni spesso in concorrenza con i Pentium di fascia bassa.
Sui Pentium, la cache interna è passata a 16 KB, divisa in 8 KB per le istruzioni (con funzionamento write-through) e 8 KB per i dati (in write-back); ma è con l'ultimo nato, il Pentium Pro, che si è avuta la vera svolta: questo processore dal particolare layout rettangolare è infatti, in realtà , un "chip doppio", integrando un processore (dotato di cache di primo livello simile a quella del Pentium) ed una cache memory write-through sincrona (vi rimando al prossimo articolo sui vari tipi di cache) da 256 KB o 512 KB.
L'integrazione di ben due livelli di cache sulla CPU è una tecnica
adottata per la prima volta su larga scala dalle CPU RISC Alpha 21164, ed ha
diverse valide giustificazioni.
Anzitutto, la grande quantità di cache memory permette di avere un eccellente
hit rate anche su sistemi pesantemente multitasking/multiutente, dove il
principio di localizzazione ha minore validità, evitando di dover accedere
spesso al bus esterno. E' questa un'esigenza sempre più importante, dal momento
che le moderne CPU sono caratterizzate da un elevato rapporto di
moltiplicazione tra il clock esterno (quello della mother board) e quello
interno (core clock), il che comporta necessariamente un notevole collo
di bottiglia quando sia necessario accedere alle memorie esterne (che siano
cache o memoria centrale), a causa della minore ampiezza di banda disponibile.
Esempio: il Pentium 200 Mhz funziona con una frequenza esterna di 66.6 Mhz su
bus dati da 64 bit, il che significa che, nel caso migliore (0 stati di
attesa), si può contare su un throughput massimo teorico di 532 MB/s (con 1 MB
= 10^6 byte). Internamente (tra CPU e cache integrata), però , il transfer rate
teorico di punta è di ben 1600 MB/s considerando un path dati interno sempre da
64 bit (in realtà , il bus interno cache-CPU è da 256 bit, dovendo fornire dati
ed istruzioni alle due unità pipelined che costituiscono il "cuore ad
interi" del Pentium; quindi il valore è , sempre sul piano teorico, 4
volte superiore!).
Il secondo grande vantaggio di avere una buona quantità di cache
integrata è evidente in sistemi multiprocessore.
Qui, l'esigenza di assicurare la giusta coerenza tra i dati in elaborazione
sulle varie CPU comporta una notevole complessità nella logica di gestione
della cache memory; se ciascuna CPU ha una buona quantità di cache integrata,
con il relativo supporto all'archittettura multiprocessore, il sistema può fare
a meno di una cache memory esterna condivisa tra le varie CPU, semplificando
nettamente la scheda madre ed abbattendo i costi. Proprio questa considerazione
ha spinto Intel a fornire fino a 512 KB di cache 2L all'interno dei Pentium
Pro, dotati di una logica di supporto per l'SMP (Simmetrical Multi Processing)
particolarmente completa.
Accessi alla memoria
Per effettuare un accesso alla memoria, la CPU deve effettuare una
serie di operazioni: si tratta, essenzialmente, di richiedere l'uso del bus,
specificare di che tipo di operazione si tratta, fornire l'indirizzo ed
effettuare il trasferimento vero e proprio (quindi leggere o scrivere le 32 o
64 linee di dati del bus).
Queste operazioni richiedono l'asserzione di determinati segnali sul bus della
CPU, la loro stabilizzazione elettrica, la loro lettura (campionamento),
interpretazione, ecc.; tutto ciò ha un "costo" in termini di cicli di
clock.
Una lettura singola (nonché la prima lettura delle 4 che compongono un ciclo burst) può essere completata, dalle CPU 486 e successive, in un minimo di 2 cicli di clock; lo stesso vale per una scrittura. Le letture e le scritture di un burst successive alla prima, invece, possono teoricamente essere completate ciascuna in un ciclo di clock: avremmo quindi un timing minimo per letture burst e scritture burst di 2-1-1-1 .
Cache associativa
La cache associativa ha, per ogni slot, un campo denominato TAG che contiene la posizione in
memoria centrale dei dati contenuti nello slot stesso.
Questo TAG viene aggiornato ogni volta che i dati nello slot cambiano, e viene
consultato ogni volta che ha luogo un accesso in memoria, confrontandolo con
l'indirizzo del dato richiesto: se c'è corrispondenza, l'accesso avrà luogo
alla cache anziché alla memoria.
Le dimensioni del campo TAG dipendono dal numero di possibili indirizzi che
deve puntare; un caso tipico (Pentium) prevede che la quantità di memoria
centrale da "coprire" sia 64 MB, e la dimensione della cache line di
32 byte. Abbiamo evidentemente 2^21 possibili zone, indirizzabili con 21 bit,
che quindi è la dimensione minima del campo TAG.
La percentuale non trascurabile di spazio occupato dal TAG (quasi il 10% della capacità totale della cache memory) e soprattutto la complessità delle operazioni da effettuare per controllare se un dato è nella cache (complessità che ha conseguenze prestazionali) hanno pregiudicato notevolmente la diffusione degli schemi a cache associativa "pura", che praticamente non è mai utilizzata nei moderni PC. Un notevole successo hanno ottenuto, invece, le architetture a mappa diretta ed associativa ad insiemi.
Cache a mappa diretta
Se nella cache associativa ogni slot poteva contenere dati provenienti da qualsiasi zona della memoria, lasciando al TAG il compito di individuarne l'indirizzo, nell'organizzazione a mappa diretta ogni slot ha una sua zona di competenza.
Le cache line di un sistema a mappa diretta sono rigorosamente
ordinate in base alla posizione, che costituisce, da un certo punto di vista,
una parte del TAG: in base all'indirizzo di memoria del dato in esame si
seleziona una ben precisa cache line che conterrà (o contiene) il dato.
Appare subito evidente che una semplice codifica posizionale non è sufficiente:
per renderla univoca (condizione necessaria per la corretta individuazione del
dato) servirebbe una cache memory grande quasi quanto l'intera memoria centrale
(ci sarebbe un rapporto 1:4 dovuto al fatto che ogni cache line ospita 4
parole)
In effetti, ad una stessa cache line "competono" più indirizzi (il
numero dipende dal rapporto tra quantità di memoria da "coprire" e
numero di slot): a selezionare quale tra questi possibili indirizzi sia quello
effettivamente in uso pensa un campo TAG; a differenza dell'architettura associativa,
perograve , il TAG deve contenere solo una parte dell'informazione relativa
all'indirizzo e quindi puograve avere dimensioni sensibilmente più ridotte.
Riprendendo l'esempio fatto con la cache associativa, supponiamo di avere 64 MB
di memoria da coprire con una cache da 256 KB, avente slot da 32 byte -avremo
quindi 8192 slot-.
Poiché (64*2^20 / 32) / 8192 = 256, avremo che ogni cache line dovrà occuparsi
di 256 possibili diversi indirizzi: basta un campo TAG da 8 bit per determinare
univocamente l'effettivo indirizzo in uso, rispetto ai 21 bit che servivano per
la cache associativa.
Un risparmio di 13 byte per slot non è male (alla fine, con i dati del nostro
esempio si risparmiano 13 KB di SRAM); tuttavia, il principale vantaggio della
cache a mappa diretta è la velocità di accesso: infatti, per verificare se un
dato è presente in cache è sufficiente comparare (in logica combinatoria,
quindi senza stati di attesa) il suo indirizzo in memoria centrale con l'unione
del numero d'ordine dello slot (bit più significativi) e del TAG (bit meno
significativi), unione effettuabile con semplice logica cablata. Poiché tale
operazione va effettuata per ogni slot, è presente un circuito speciale che
consente di confrontare in parallelo gli indirizzi di tutti gli slot della
cache (il che rende la cache a mappa diretta leggermente più complessa a
livello hardware rispetto a quella associativa).
In scrittura, l'operazione è ancora più rapida: filtrando in logica cablata
l'indirizzo in memoria centrale del dato in esame si ottengono subito il numero
d'ordine dello slot interessato (bit più significativi) e il valore da
immettere nel campo TAG (bit rimanenti).
La cache a mappa diretta non è tuttavia esente da difetti: il
principali sono una scarsa "flessibilità " alle dimensioni della
memoria centrale da coprire (derivante dalla necessità di suddividere in modo
preciso l'indirizzo del dato in un numero fissato di bit dati dal numero
d'ordine -e quindi dal numero di slot, che sono in quantità fissa- e dal TAG) e
i conflitti
di cache generati dal fatto che ogni slot deve occuparsi di più zone di
memoria: se un dato è presente nella cache, i suoi "antagonisti" (con
indirizzo che afferisce a quello stesso slot) non potranno sicuramente esserci.
Ciograve potrebbe creare una situazione paradossale in cui, se il working set è
costituito da soli dati ad indirizzi in multipli del numero di slot, un solo
slot della cache si trova a "tamponare" l'intero set di dati !
Questa è , appunto, una situazione paradossale; tuttavia il principio del
conflitto resta valido, ed in effetti l'hit rate di una cache a mappa diretta è sempre
inferiore a quello di una cache associativa di pari dimensioni.
Per tentare di risolvere il problema, si è cercata una soluzione intermedia: l'organizzazione associativa ad (N) insiemi ( N-way set associative ).
Cache associativa ad insiemi
Immaginiamo di avere N cache associative affiancate, con le "colonne" di slot scelte dal valore di un TAG, ed avremo lo schema di una cache associativa ad N insiemi: una via di mezzo tra la velocità di accesso di una cache a mappa diretta e l'elevato hit rate di una associativa pura; guardando da un altro punto di vista, se riduciamo ad 1 il numero di insiemi di una cache di questo tipo, riotteniamo la cache associativa; se riduciamo ad 1 il numero delle righe, invece, abbiamo una cache a mappa diretta.
Chiaramente, se aumentiamo troppo il numero degli insiemi aumenta anche il numero di potenziali conflitti di cache, riducendo l'hit rate: per questo motivo N vale in genere, nei sistemi in esame, 2 o 4.
Privacy |
Articolo informazione
Commentare questo articolo:Non sei registratoDevi essere registrato per commentare ISCRIVITI |
Copiare il codice nella pagina web del tuo sito. |
Copyright InfTub.com 2025