CPU cache: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Zorro55 (discussione | contributi)
Correzioni SOS varie
Xr1blu (discussione | contributi)
Funzionalità collegamenti suggeriti: 2 collegamenti inseriti.
 
(38 versioni intermedie di 3 utenti non mostrate)
Riga 1:
{{NN|informatica|febbraio 2013}}
{{organizzare|La voce è trattata in maniera eccessivamente specialistica. Bisognerebbe semplificarla per non renderla troppo simile a quanto si trova su un manuale specialistico di informatica e migliorarne la leggibilità per un utente non tecnico|Informatica|Maggio 2018}}
{{Aggiornare|argomento=informatica|commento=Buona parte della voce è ferma al 2004, e parla pure al futuro...}}
{{Correggere|informatica|settembre 2019}}
{{vedi anche|Memoria cache}}
La Cache è un tipo di [[RAM|memoria RAM]] di piccole dimensioni, progettata per essere molto veloce ma anche più costosa rispetto ad altri tipi di memoria. Con l’aumento delle prestazioni dei processori e l’uso crescente di tecnologie avanzate come l’[[intelligenza artificiale]] e il [[cloud computing]], la cache continua a giocare un ruolo fondamentale. Quando si parla di "lato prestazioni", si intende che la cache aiuta il computer o il dispositivo a lavorare più rapidamente: conserva temporaneamente le informazioni e i dati usati più spesso, permettendo al [[processore]] di accedervi immediatamente senza doverli recuperare da memorie più lente o da internet. Questo rende le operazioni più veloci, migliora l’[[efficienza energetica]] e garantisce una migliore esperienza d’uso, anche con applicazioni moderne e complesse. <ref name=":0">{{Cita libro|titolo="Computer Architecture: A Quantitative Approach" di John L. Hennessy e David A. Patterson (5ª edizione, Morgan Kaufmann, 2012, e successive ristampe aggiornate).}}</ref>
 
== Caratteristiche ==
[[File:Cache,basic.svg|thumb|left|Confronto tra memoria RAM e memoria CPU cache]]
 
Nel diagramma a sinistra sono illustrate due memorie: la memoria principale e la cache. La cache è una memoria molto più veloce, ma anche più piccola, usata per conservare temporaneamente i dati più usati dal processore.
Nel diagramma a sinistra sono illustrate due memorie: la memoria principale e la cache. La cache è una memoria molto più veloce, ma anche più piccola, usata per conservare temporaneamente i dati più usati dal processore. Ogni posizione della cache contiene un dato chiamato linea di cache (o blocco di cache), che attualmente può variare da 512 [[byte]] a 8 [[Megabyte|MB]], a seconda del tipo e livello di cache. Al contrario, la memoria principale ha dimensioni molto più grandi, in genere tra 1 e 16 [[Gigabyte|GB]] o più, ma è anche più lenta. Ogni posizione in memoria è identificata da un numero univoco chiamato indice (o indirizzo di memoria). Nella cache, a ogni linea è associata un’etichetta (o tag), che indica a quale indirizzo della memoria principale corrisponde quel dato. Quando il processore ha bisogno di un'informazione, controlla prima se si trova nella cache. Se la trova (si parla di cache hit), l’accesso è molto veloce. Se invece non è presente (si parla di cache miss), il dato viene preso dalla memoria principale, che è più lenta, e copiato nella cache per usi futuri. Finché la maggior parte dei dati richiesti si trovano nella cache, il tempo medio per accedere alla memoria (latenza) resta basso, vicino a quello rapidissimo della cache. Questo si traduce in prestazioni migliori per il sistema. Quando il processore necessita di leggere o scrivere in una data collocazione in memoria principale, inizialmente controlla se il contenuto di questa posizione è caricato in cache. Questa operazione viene effettuata confrontando l'indirizzo della posizione di memoria con tutte le etichette nella cache che potrebbero contenere il dato a quell'indirizzo. Se il processore trova che la posizione di memoria è in cache, si parla di cache hit (accesso avvenuto con successo), altrimenti di cache miss (fallimento dell'accesso). Nel caso di un cache hit, il processore legge o scrive immediatamente il dato sulla linea di cache. Il rapporto tra cache hit e accessi totali è chiamato anche hit rate ed è una misura indiretta dell'efficacia dell'algoritmo di cache.
 
Ogni posizione della cache contiene un dato chiamato linea di cache (o blocco di cache), che attualmente può variare da 512 [[byte]] a 8 [[Megabyte|MB]], a seconda del tipo e livello di cache. Al contrario, la memoria principale ha dimensioni molto più grandi, in genere tra 1 e 16 [[Gigabyte|GB]] o più, ma è anche più lenta.<ref name=":0" />
Nel caso di un cache miss, ne consegue (per la maggior parte delle cache) la creazione di una nuova entità, che comprende l'etichetta appena richiesta dal processore e una copia del dato nella memoria principale. Un fallimento del genere è relativamente lento, in quanto richiede il trasferimento del dato dalla memoria principale, in taluni casi dopo avere cancellato il dato non più valido.
 
Ogni posizione in memoria è identificata da un numero univoco chiamato indice (o indirizzo di memoria). Nella cache, a ogni linea è associata un’etichetta (o tag), che indica a quale indirizzo della memoria principale corrisponde quel dato.
Questo è il motivo per cui una cache molto capiente, anche se gestita con un algoritmo efficiente, in alcune circostanze può essere controproducente in termini di prestazioni. Infatti, il processo di cancellazione di un dato in cache, scaduto e non più valido, e il caricamento nella cache del dato corretto richiede tipicamente più tempo rispetto a quando la CPU legge il dato direttamente dalla memoria principale senza valersi della cache. In altre parole, una cache ampia può comportare, in specifiche situazioni di calcolo, per esempio non iterativi, un numero superiore di cache miss che di cache hit, con un sensibile degrado delle prestazioni.
 
Quando il processore ha bisogno di un'informazione, controlla prima se si trova nella cache. Se la trova (si parla di cache hit), l’accesso è molto veloce. Se invece non è presente (si parla di cache miss), il dato viene preso dalla memoria principale, che è più lenta, e copiato nella cache per usi futuri.
 
Finché la maggior parte dei dati richiesti si trovano nella cache, il tempo medio per accedere alla memoria (latenza) resta basso, vicino a quello rapidissimo della cache. Questo si traduce in prestazioni migliori per il sistema. Quando il processore necessita di leggere o scrivere in una data collocazione in memoria principale, inizialmente controlla se il contenuto di questa posizione è caricato in cache. <ref name=":0" />
 
Questa operazione viene effettuata confrontando l'indirizzo della posizione di memoria con tutte le etichette nella cache che potrebbero contenere il dato a quell'indirizzo. Se il processore trova che la posizione di memoria è in cache, si parla di cache hit (accesso avvenuto con successo), altrimenti di cache miss (fallimento dell'accesso).
 
Nel caso di un cache hit, il processore legge o scrive immediatamente il dato sulla linea di cache. Il rapporto tra cache hit e accessi totali è chiamato anche hit rate ed è una misura indiretta dell'efficacia dell'algoritmo di cache.
 
Quando si verifica una cache miss — cioè il processore richiede un dato assente dalla cache — viene creata una nuova voce che contiene l’etichetta (o ''tag'') e una copia del dato recuperata dalla memoria principale. L’operazione è più lenta rispetto all’accesso diretto alla cache, perché richiede il trasferimento del dato e, spesso, la sostituzione di un dato ormai inutile. <ref name=":0" />
 
Per decidere quale dato eliminare per far posto al nuovo, la cache adotta una regola detta [[Euristica (informatica)|euristica]] di rimpiazzamento: si tratta di un metodo approssimato per cercare di prevedere quali dati saranno meno utili in futuro. Tuttavia, anche nel 2025, nessuna euristica è perfetta, soprattutto perché le cache hardware devono basarsi su meccanismi semplici da implementare nei circuiti elettronici. Per esempio, una delle politiche più diffuse è la [[Least Recently Used|LRU]], che rimuove il dato utilizzato meno recentemente, con l’idea che sia il meno probabile da riutilizzare a breve.
 
Proprio per questo motivo, una cache molto capiente, anche se controllata da un algoritmo sofisticato, può risultare controproducente in alcune situazioni. Ad esempio, quando il processore lavora con dati che cambiano spesso (come in calcoli non ripetitivi), la cache può riempirsi di informazioni che non verranno più utilizzate.
 
Ogni nuovo dato deve quindi essere caricato e gestito con l’ulteriore costo di rimuovere dati obsoleti, e questo può rallentare il sistema. In certi casi, il tempo complessivo impiegato per questo processo può essere addirittura superiore a quello richiesto per leggere direttamente il dato dalla memoria principale.
 
In altre parole, anche oggi una cache più grande non garantisce sempre prestazioni migliori: in scenari particolari può generare più cache miss che cache hit, con un impatto negativo sulla velocità complessiva del sistema. <ref name=":0" />
 
== Alcuni dettagli operativi ==
Per poter far spazio a nuovi dati nel caso di un ''cache miss'', la cache generalmente deve eliminare il contenuto di una delle linee. L'[[Euristica (informatica)|euristica]] che utilizza per scegliere quale dato eliminare è chiamata politica di rimpiazzamento. Il problema fondamentale di ogni politica di rimpiazzamento è quello di dover predire il dato della cache che verrà richiesto nel futuro con minor probabilità.
 
Quando si verifica una cache miss — ovvero quando il processore richiede un dato non presente nella cache — è necessario liberare spazio per poter memorizzare il nuovo dato. Questo comporta l’eliminazione di uno dei dati già presenti nella cache. La scelta di quale dato rimuovere è guidata da una regola detta politica di rimpiazzamento (replacement policy).
Predire il futuro è difficile, soprattutto per le cache hardware che devono sfruttare regole facilmente implementabili in circuiteria, perciò esistono una serie di politiche di rimpiazzamento e nessuna di esse può essere ritenuta perfetta. Una delle più popolari, la [[Least Recently Used|LRU]] (dall'inglese ''Least Recently Used'', cioè ''usato meno recentemente''), rimpiazza, appunto, il dato al quale si è fatto accesso meno recentemente.
 
Lo scopo di questa politica è stimare quale dato sia meno probabile che venga riutilizzato a breve, così da poterlo sostituire senza compromettere le prestazioni complessive del sistema. Tuttavia, questa previsione è intrinsecamente complessa, in particolare per le cache hardware, che devono adottare strategie semplici, rapide e facilmente implementabili a livello di circuito.<ref name=":0" />
 
Sono state sviluppate diverse politiche di rimpiazzamento, ciascuna con punti di forza e debolezze, ma nessuna in grado di garantire risultati ottimali in ogni contesto operativo. Una delle più diffuse è la [[Least Recently Used|LRU]] (Least Recently Used), che rimuove il dato utilizzato meno di recente, basandosi sull’ipotesi che un dato non usato da tempo difficilmente verrà richiesto di nuovo a breve. Pur non essendo ideale in ogni scenario, questa soluzione rappresenta un buon compromesso tra semplicità ed efficacia.
 
Quando un dato viene modificato nella cache, deve essere prima o poi aggiornato anche nella [[Memoria (informatica)#Memoria primaria|memoria principale]], per mantenere la coerenza tra i due livelli di memoria. Il momento in cui avviene questa operazione dipende dalla cosiddetta politica di scrittura (write policy).
Quando un dato è scritto nella cache, dopo un po' di tempo deve comunque essere scritto in [[Memoria (informatica)#Memoria primaria|memoria principale]]. La decisione del momento in cui questa scrittura deve aver luogo è controllata dalla politica di scrittura. In una cache ''write-through,'' ogni scrittura sulla cache comporta una scrittura contemporanea nella memoria principale. In alternativa, una cache ''write-back'' non esegue immediatamente questa azione: al contrario, la cache tiene traccia delle linee che contengono dati da aggiornare settando opportunamente quello che viene chiamato il ''dirty bit''. Il dato viene effettivamente scritto in memoria solo quando esso deve essere eliminato dalla cache per far spazio a nuove informazioni. Per questa ragione, una ricerca fallita in una cache write-back spesso genera due accessi alla memoria: uno per leggere il nuovo dato, l'altro per scrivere la vecchia informazione (se indicato dal dirty bit). Sia il write-back, sia il write-through servono a mantenere la coerenza tra i livelli di memoria.
In una cache di tipo write-through, ogni modifica effettuata nella cache viene immediatamente replicata anche nella memoria principale. Questo garantisce una maggiore coerenza tra i dati, ma può rallentare le prestazioni a causa dell’elevato numero di accessi alla memoria. Al contrario, in una cache write-back, la scrittura nella memoria principale non avviene subito. I dati modificati restano temporaneamente nella cache e vengono segnati come "sporchi" tramite un apposito indicatore, chiamato dirty bit. Solo quando quella specifica linea di cache deve essere sostituita — ad esempio per far posto a nuovi dati — il contenuto aggiornato viene scritto nella memoria principale. Questo approccio riduce il numero di scritture, migliorando l’efficienza, ma richiede un meccanismo più complesso per garantire la coerenza. In un sistema write-back, una cache miss può generare due accessi alla memoria: uno per scrivere il dato modificato (se segnalato dal dirty bit) e uno per leggere il nuovo dato richiesto. <ref name=":0" />
 
Esistono anche alcune politiche intermedie.ibride, Lache cachecercano potrebbeun esserecompromesso adtra efficienza e coerenza. Un esempio è una cache write-through con buffer di scrittura, madove le scritture potrebberovengono essereinserite temporaneamente inserite in una coda, cosìper dapoi processareessere insiemeinviate scritturealla memoria principale in modo più multipleefficiente, ottimizzandoad esempio raggruppandole o gestendole in momenti di basso l'accessotraffico alsul [[Bus (informatica)|bus di sistema]]. <ref>{{Cita libro|titolo=Robert C. Steinke & Gary J. Nutt, A Unified Theory of Shared Memory Consistency, Journal of the ACM, Volume 51, Issue 6, 2004.}}</ref>
 
In un sistema [[multiprocessore]], è possibile che i dati contenuti nella memoria principale, di cui esiste una copia nella cache, vengano modificati da altri processori. In questi casi, le copie locali nella cache possono diventare obsolete o incoerenti. Per evitare errori di calcolo, i sistemi devono garantire che tutte le cache coinvolte lavorino con dati aggiornati. A tal fine vengono utilizzati appositi protocolli di coerenza, che regolano la comunicazione tra le cache per mantenere l’allineamento corretto dei dati. <ref>{{Cita libro|titolo=ijay Nagarajan, Daniel J. Sorin, Mark D. Hill & David A. Wood – A Primer on Memory Consistency and Cache Coherence, Second Edition (Morgan & Claypool Publishers, 2020)}}</ref>
I dati in memoria principale, dei quali esiste una copia nella cache, potrebbero essere modificati da altre cause (evento non improbabile, ad esempio, in un sistema [[multiprocessore]]), perciò i dati nella cache potrebbero diventare obsoleti. I protocolli di comunicazione tra i sistemi di gestione delle cache che conservano la consistenza dei dati sono chiamati protocolli di coerenza.
 
IlUn altro aspetto critico è il tempo impiegatonecessario per leggere un dato dalla memoria, (lanoto come latenza di lettura). èQuesto importante,tempo perchéinfluisce spessodirettamente unasulle CPUprestazioni potrebbedel completareprocessore: se la propriaCPU codadeve diattendere operazionitroppo mentrea aspettalungo l'arrivol’arrivo deldi un dato, richiesto.rischia Quandodi unfermarsi microprocessoretemporaneamente, raggiungeentrando questoin uno stato, si parla dichiamato stallo della CPU. ConTale l'aumentosituazione dellaè velocitàparticolarmente deidannosa microprocessori, l'andare in stallo per cache miss spreca molta potenza di calcolo; lenelle CPU moderne, infatti,capaci possonodi eseguire centinaia d'istruzionidi nelloistruzioni stessonel tempo necessarioimpiegato per caricare un singolo dato dallada una memoria lenta. SonoPer stateridurre studiate,l’impatto perciòdegli stalli, variesono state sviluppate tecniche perdi "tenereottimizzazione occupata"che lapermettono alla CPU durantedi questasfruttare il tempo fased’attesa. AlcuniAd microprocessoriesempio, comealcuni ilmicroprocessori adottano l’[[Pentiumesecuzione Profuori ordine]] (out-of-order execution), tentanoche consente di eseguireportare leavanti operazioni chesuccessive seguonoa quella chein staattesa, aspettandopurché il dato, sesiano indipendenti dadal essadato (per questo in inglese sono chiamati ''[[Esecuzione fuori ordine|out-of-order]]'')mancante. IlAltri [[Pentiumprocessori 4]] usaimplementano il [[multithreading]] simultaneo, (chiamatocome nel caso della tecnologia [[HyperThreading]] nella terminologiadi [[Intel]]), che permetteconsente a un altrosecondo programmaflusso di usareistruzioni la(thread) CPUdi utilizzare l’unità di calcolo mentre unil primo programmaè stain aspettandoattesa l'arrivodei dati. In questo modo, si riduce lo spreco di daticicli dalladi memoriaCPU, principalemigliorando l’efficienza del sistema. <ref name=":0" />
 
== Associatività ==
[[File:Cache,associative-fill-both.png|thumb|upright=2.0|Quali posizioni di memoria possono essere caricate in quali posizioni della cache]]
 
La politica di rimpiazzamento decidedetermina dove, nella cache, può risiedereessere memorizzata una copia di unaun particolaredato posizione didella memoria principale. Se taleil politicadato èpuò liberaessere di scegliereinserito in qualequalsiasi linea di cache caricare il dato, la cache vieneè chiamatadetta ''fully associative'' (o anche completamente associativa). Invece,Se se ogni dato in memoriainvece può essere posizionatocollocato solo in una particolare linea di cachespecifica, essasi èparla dettadi cache ''direct mapped''. (oUn anchecompromesso atra mappaturai diretta).due Laapprocci maggiorè parte dellela cache, peròset associative, implementadove unogni compromessodato chiamatopuò ''setessere associative''inserito (oin ancheuna parzialmentedelle linee di un associativagruppo (set). PerAd esempio, la cache dati di livelloprimo 1livello dell'dell’[[Advanced Micro Devices|AMDAthlon]] di [[AthlonAMD]] è ''2-way set associative'', cioè unaogni particolare posizione di memoriadato può essere caricatarisiedere in cachedue in duelinee distinte posizionidel nellaset cachecorrispondente. dati<ref diname=":0" livello 1./>
 
Se ogni posizione indella memoria principale può essere caricatamemorizzata in due posizionilinee diverse, ladella domandacache sorge(come spontanea:nel caso di una cache 2-way set associative), è naturale chiedersi quali? Losiano schemaqueste utilizzatodue posizioni. Il metodo più frequentementecomune per stabilirlo è mostratoquello illustrato nel diagramma a lato: i bit meno significativi dell'indice della posizionedell’indirizzo di memoria vengono usati come indici per ladeterminare cachea equale aset ognunoappartiene diil questidato. indiciA sonociascun associateset corrispondono due linee di cache disponibili. UnaUn buona proprietàvantaggio di questo schema è che, leconoscendo etichettegià deiparte datidell’indirizzo caricati(quella inusata cacheper nonselezionare devonoil includereset), quellale parteetichette dell'indice(tag) giàsalvate codificatain dallacache lineapossono diessere cachepiù scelta.brevi, Poichépoiché inon tagdevono sonoripetere espressil’informazione sugià menocodificata. bit,Questo occupanocomporta menoun uso più efficiente della memoria cache e iluna riduzione del tempo necessario per processarliconfrontare èi minoretag durante l’accesso. <ref name=":0" />
 
Sono stati proposti schemi alternativi di organizzazione della cache per ridurre le collisioni, cioè i casi in cui più dati competono per la stessa posizione. Un esempio è la skewed cache, in cui il metodo di selezione del set varia tra le diverse vie (ways). Nella way 0, l’indice è calcolato con il metodo tradizionale (ad esempio usando i bit meno significativi dell’indirizzo), mentre nella way 1 l’indice è ottenuto tramite una [[funzione di hash]]. Una buona funzione di hash distribuisce in modo più uniforme gli indirizzi, riducendo il rischio che dati frequentemente usati entrino in conflitto tra loro, come può accadere con la mappatura diretta.Lo svantaggio di questo approccio è il ritardo aggiuntivo dovuto al calcolo della funzione di hash. Inoltre, può diventare più complesso gestire l’eliminazione delle linee obsolete, perché le diverse vie possono usare criteri di assegnazione diversi, rendendo meno efficiente il tracciamento dell’utilizzo recente (ad esempio nel caso di strategie come la Least Recently Used, o LRU). <ref>{{Cita libro|titolo=Norman P. Jouppi
Sono stati suggeriti altri schemi, come quello della ''skewed cache'', dove l'indice della ''way 0'' è diretto, come sopra, mentre l'indice per la ''way 1'' è calcolato attraverso una funzione di hash. Una buona funzione di hash ha la proprietà che gli indirizzi che sono in conflitto con il direct mapping tendono a non collidere quando sono mappati con la funzione di hash, così è meno probabile che un programma soffra di un numero imprevedibilmente grande di collisioni dovuti a un metodo d'accesso particolarmente patologico. Lo svantaggio è il ritardo aggiuntivo necessario per calcolare il risultato della funzione di hash. In aggiunta, quando diventa necessario caricare una nuova linea ed eliminarne una vecchia, potrebbe rivelarsi difficile determinare quale tra le linee esistenti è stata usata meno recentemente, in quanto la nuova linea entra in conflitto con differenti "set" di linee per ogni "way"; il tracciamento LRU è infatti normalmente calcolato per ogni set di linee.
Improving direct-mapped cache performance by the addition of a small fully-associative cache and skewed-associative caches, Proceedings of the 17th Annual International Symposium on Computer Architecture (ISCA), 1990.}}</ref>
 
In generale, il grado di associatività della cache rappresenta un compromesso tra prestazioni e complessità. Maggiore associatività significa che ogni dato può essere memorizzato in più posizioni all’interno di un set, riducendo la probabilità di cache miss. Tuttavia, questo comporta anche un maggiore costo in termini di tempo, energia e spazio, poiché il sistema deve controllare più posizioni a ogni accesso.
L'associatività rappresenta un compromesso. Se ci sono dieci posizioni, la politica di rimpiazzamento può riempire una nuova linea, ma quando si cerca un dato, è necessario controllare tutte e 10 le posizioni. La necessità di controllare più posizioni richiede più potenza, spazio e tempo. D'altra parte, le cache con più associatività soffrono di meno di cache miss (di cui si parlerà nel paragrafo successivo). La regola generale è che raddoppiare l'associatività ha circa lo stesso effetto sull'hit rate del raddoppiare della dimensione della cache, passando da 1-way (direct mapping) a 4-way. Aumenti dell'associatività oltre il 4-way hanno un impatto molto meno significativo sull'hit rate e sono generalmente utilizzati per altri motivi (come il virtual aliasing, trattato nella sezione "Traduzione degli Indizi").
Un principio empirico largamente accettato è che raddoppiare l’associatività (ad esempio passando da una cache direct-mapped a una 2-way o 4-way set associative) produce benefici simili a raddoppiare la dimensione della cache in termini di aumento del tasso di successo (hit rate). Tuttavia, oltre il 4-way, i vantaggi tendono a ridursi, e associatività più elevate vengono usate soprattutto per gestire casi specifici come il virtual aliasing (descritto nella sezione dedicata alla Traduzione degli Indirizzi). <ref name=":1">{{Cita libro|titolo=Hennessy, John L. e Patterson, David A.
Computer Architecture: A Quantitative Approach, 6ª edizione, Morgan Kaufmann, 2017 (ultima edizione aggiornata).}}</ref>
 
Uno dei vantaggi delladelle cache direttamentea mappata,mappatura diretta è chela permettepossibilità un'esecuzionedi eseguire operazioni in modo speculativo in speculativamodo semplice e velocerapido. Una volta calcolato l'indirizzol’indirizzo di memoria, è subito noto quale linea didella cache potrebbe contenere il dato richiesto. QuestaQuesto puòconsente essereal lettaprocessore edi illeggere processorepreventivamente puòquella continuarelinea e iniziare a lavorare con quelsul dato, anche prima di completare il controllo per verificare se l'etichettal’etichetta corrisponde(tag) memorizzata corrisponda effettivamente all'indirizzoall’indirizzo richiesto. <ref name=":1" />
 
L'ideaQuesto cheprincipio ilpuò processoreessere utilizziesteso ianche dati inalle cache primaassociative, ancorasebbene dicon verificaremaggiore lacomplessità. corrispondenzaIn traquesti etichettacasi, e indirizzo può essere applicata anche alle cache associative. Unun sottoinsieme dell'etichettadell’etichetta, chiamato hintcomunemente in inglesehint, può essere utilizzato temporaneamenteusato per selezionare provvisoriamente una delle linee dicandidate cacheall’interno associatedel all'indirizzoset richiestoassociato all’indirizzo. QuestoLa datoCPU può esserecosì utilizzatoiniziare dallaad CPUutilizzare intemporaneamente paralleloquel dato, mentrein l'etichettaparallelo vienealla controllataverifica completamentecompleta dell’etichetta. Questa tecnica funzionaspeculativa meglioè particolarmente utile quando èviene utilizzataimpiegata nelinsieme contestoai dellameccanismi di traduzione degli indirizzi, comepermettendo spiegatoalla piùCPU avantidi anticipare l'accesso ai dati, riducendo i ritardi dovuti alla latenza di memoria. <ref name=":1" />
 
== Cache miss ==
Con "fallimento della cache" (in inglese ''cache miss'') ci si riferisce a un intento fasullo nel leggere o scrivere un pezzo di dati nella cache, che ha come risultato una latenza molto più lunga nell'accesso alla memoria principale. Per un fallimento nella lettura dalla cache istruzioni, il processore deve aspettare (''stallo'') finché l'istruzione non è caricata dalla memoria principale. Un fallimento della cache causato dal caricamento di un dato può invece essere meno doloroso, perché le altre istruzioni non correlate a esso possono comunque essere eseguite, finché l'operazione che richiede i dati da caricare può essere eseguita. Comunque, i dati sono spesso usati immediatamente dopo l'istruzione di caricamento. L'ultimo caso di ''cache miss'', cioè un fallimento in scrittura, è il meno preoccupante, perché di solito la scrittura è bufferizzata. Il processore può continuare tranquillamente finché il buffer non è pieno. (Non esiste un fallimento nella scrittura della cache istruzioni perché esse sono di sola lettura.)
 
Con il termine cache miss (in italiano fallimento della cache) si indica una situazione in cui il processore tenta di leggere o scrivere un dato nella cache, ma quel dato non è presente, e deve quindi essere recuperato dalla memoria principale, con un conseguente aumento della latenza. Se la cache miss riguarda una lettura di istruzioni, il processore è costretto ad attendere il caricamento dell’istruzione dalla memoria principale, entrando in uno stato di stallo. Se invece la cache miss avviene durante il caricamento di un dato, il rallentamento può essere meno grave, perché il processore può continuare a eseguire altre istruzioni non dipendenti da quel dato, almeno fino a quando l'operazione che lo richiede diventa eseguibile. Tuttavia, in molti casi, i dati caricati vengono utilizzati immediatamente dopo l’istruzione di caricamento, il che rende comunque l’attesa penalizzante. Le cache miss in scrittura, infine, sono generalmente meno problematiche, perché le scritture vengono bufferizzate: il processore può proseguire l’esecuzione mentre i dati vengono scritti in secondo piano, salvo che il buffer non si riempia. Da notare che non esistono cache miss in scrittura per le cache istruzioni, in quanto le istruzioni sono solitamente di sola lettura e non vengono mai scritte direttamente nella cache da parte del processore.<ref name=":1" />
Per minimizzare la frequenza di ''cache miss'', un grande sforzo di analisi è stato fatto sul comportamento della cache per trovare la miglior combinazione di dimensione, associatività, dimensione dei blocchi e così via. Sequenze di referenze di memoria create dai programmi di [[Benchmark (informatica)|benchmark]] sono salvati come ''address traces''. Ulteriori analisi simulano molte differenti possibilità d'implementazione della cache basate su queste lunghe ''address traces''. Far capire come le molteplici variabili modifichino la frequenza di [[cache hit]] può risultare abbastanza confusionario.
 
Per ridurre la frequenza delle cache miss, è stato svolto un ampio lavoro di analisi sul comportamento della cache, con l’obiettivo di identificare la combinazione ottimale di dimensione, associatività, dimensione dei blocchi e altri parametri. Per studiare queste variabili, i ricercatori utilizzano le sequenze di accesso alla memoria prodotte dai programmi di [[benchmark (informatica)|benchmark]], salvate sotto forma di tracce di indirizzi (address traces). Su queste tracce vengono simulate diverse configurazioni di cache, per valutare come ciascun parametro influenzi il numero di [[cache hit|accessi riusciti]] e di cache miss. <ref name=":1" />
Un contributo significante fu fatto da Mark Hill, il quale separò i vari fallimenti della cache in tre categorie (conosciute come ''"le tre C"''):
* ''Compulsory misses'' sono quei fallimenti causati dalla prima referenza a un dato. La dimensione della cache e la associatività non fanno differenze al numero di ''compulsory misses''. Il prefetching può aiutare qui, così come lo possono fare larghe dimensioni dei blocchi della cache (che sono un tipo di prefetching).
* ''Capacity misses'' sono quei fallimenti che una cache di una data dimensione avrà, a dispetto dell'associatività o della dimensione del blocco. La curva della frequenza dei ''capacity misses ''rispetto alla dimensione della cache fornisce una qualche misura della località temporanea di un particolare flusso di referenze.
* ''Conflict misses'' sono quei fallimenti che avrebbero potuto essere evitati se la cache non avesse ripulito un dato precedentemente. I ''conflict misses'' potrebbero essere ulteriormente divisi in ''mapping misses'', che sono inevitabili data una particolare associatività, e ''replacement misses'', che sono causati dalla particolare scelta della regola di rimpiazzamento.
 
Tuttavia, comprendere l’effetto combinato di tutte queste variabili può risultare complesso. Un contributo fondamentale è stato fornito da Mark Hill, che ha proposto una classificazione delle cache miss in tre categorie principali, note come le "tre C":
* Compulsory misses (miss obbligate): si verificano alla prima richiesta di un dato mai caricato prima nella cache. Questi fallimenti sono indipendenti dalla dimensione o dall’associatività della cache. Possono essere mitigati con tecniche di prefetching o aumentando la dimensione dei blocchi, che di fatto prelevano più dati in una sola operazione.
* Capacity misses (miss da capacità): avvengono quando la cache non è abbastanza grande da contenere tutti i dati attivamente utilizzati. Questi fallimenti dipendono unicamente dalla dimensione totale della cache, e analizzarne la frequenza in funzione della dimensione permette di valutare la località temporale degli accessi.
* Conflict misses (miss da conflitto): si verificano quando due o più dati che potrebbero coesistere nella cache si contendono la stessa posizione, costringendo la cache a sovrascrivere dati ancora utili. Sono tipici delle cache non completamente associative. Questi possono essere ulteriormente suddivisi in:
* * Mapping misses, dovuti alla struttura stessa della cache (es. direct mapped);
* * Replacement misses, causati dalla scelta della politica di rimpiazzamento, che può espellere un dato che sarebbe stato riutilizzato a breve. <ref name=":1" />
[[File:Cache,missrate.png|thumb|Frequenza di fallimento (''miss rate'') a confronto con la dimensione della cache (Cache size) sulla porzione degli interi di SPEC CPU2000]]
 
Il grafico a destra riassume la performance della cache vista dai benchmarks della porzione degli interi di un SPEC CPU2000, ripresa da Hill e Cantin [http://www.cs.wisc.edu/multifacet/misc/spec2000cache-data/ Cache performance of SPEC CPU2000]. Questi benchmark servono a rappresentare il tipo di carico di lavoro che una postazione di lavoro potrebbe subire un giorno qualsiasi. In questo grafico possiamo vedere i differenti effetti delle ''tre C''.
 
All'estrema destra, quando la cache size assume un valore "Inf" (che, in altre parole, tende all'infinito), abbiamo i ''compulsory misses''. Se volessimo migliorare le caratteristiche dello SpecInt2000, aumentare la dimensione della cache oltre 1MB sarebbe praticamente inutile.
 
La frequenza di fallimento dellain una ''cache fully-associative'' rappresenta(completamente aassociativa) pienoriflette lain frequenzamodo deidiretto il numero di ''capacity misses'', cioè i casi in cui la cache non è abbastanza grande per contenere tutti i dati attivamente usati, indipendentemente dalla struttura della cache stessa. Nelle simulazioni, èsi stataadotta sceltacomunemente una regolapolitica di rimpiazzamento LRU: questo(Least mostraRecently Used), che perrimuove minimizzareil ladato frequenzameno deiutilizzato di recente. Tuttavia, anche questa strategia non è sempre ottimale. Per ottenere il minimo teorico possibile di ''capacity misses'', sarebbe necessaria una regolapolitica di rimpiazzamento perfetta, comecapace sedi adprevedere esempiocon unesattezza veggentequali indagassedati non verranno più utilizzati nel prossimo futuro. perUna trovaretale unaprevisione, posizionenaturalmente, dellaè cacheirrealizzabile chein nonpratica, stiama viene spesso evocata come ipotesi ideale — come se un "veggente" potesse scrutare il futuro per esserefare utilizzatala scelta perfetta.
 
NotareNel come,grafico nellache nostrarappresenta approssimazione dellala frequenza dei ''capacity misses'', ilsi graficopuò abbiaosservare una brusca cadutadiminuzione tra i 32KB32 KB e i 64KB64 KB di dimensione della cache. Questo indicacomportamento suggerisce che il benchmark ha un [[working set]] del benchmark — ovvero l’insieme di dati usati attivamente in un dato periodo — sia di circa 64KB64 KB. Un progettista di cache, esaminando questiquesto benchmarkrisultato, sarebbepotrebbe essere fortemente tentatoincentivato d'impostarea lascegliere una dimensione della cache appena soprasuperiore a 64 KB, così da ridurre significativamente i 64KBfallimenti. Al contrario, piuttostoimpostare chela cache appena al di sotto di questo valore. Bisognacomporterebbe notareun aumento netto della frequenza di cache miss.È inoltre importante notare che, suin questa simulazione, nessun tipolivello di associatività può far andarerende una cache ada 32KB32 KB beneefficace comequanto una cache da 64KB64 KB con associatività 4-way, o addirittura come una cache direct-mapped da 128KB128 KB. Ciò evidenzia come la dimensione della cache sia spesso più determinante dell’associatività, almeno oltre certi limiti.
 
Infine, notaresi osserva che tra i 64KB64 KB ede 1MB1 MB c'èdi dimensione, esiste una grandenotevole differenza tradi laprestazioni cachetra dile tipocache direct-mapped e quellaquelle fully-associative. Questa differenza è ladovuta principalmente alla frequenza dei ''conflict misses''miss, cioè ai fallimenti causati dal fatto che più dati si contendono la stessa posizione nella cache. Secondo i dati deldisponibili 2004nei primi anni 2000, le cache di secondo livello montate(L2) integrate direttamente sul [[circuito integrato|chip]] del processore tendonotendevano a starerientrare proprio in questo intervallo di valori,dimensioni. in quanto leLe cache più piccole, sonoinfatti, velocisono abbastanza rapide da essere utilizzate come cache di primo livello (L1), mentre quelle piùmolto grandi sonorisultano troppo costose pere esserecomplesse montateda integrare economicamentefisicamente sul chip. stessoAd esempio, il processore (l'[[Itanium 2]] hamontava una cache di terzo livello (L3) da 9MB9 MB, che all’epoca era la più grande cache on-chip disponibile sul mercato nel 2004). Dal punto di vista delladell’efficienza, frequenzaquesti deidati ''conflict misses'', risultamostrano che launa cache di secondo livello traebeneficia unnotevolmente grandedi beneficiouna dall'altamaggiore associatività, in quanto questa riduce sensibilmente i conflitti tra dati e quindi la frequenza di cache miss, migliorando le prestazioni complessive del sistema.
 
QuestoIl beneficiovantaggio dell’alta associatività nelle cache era bengià conosciutonoto neialla tardifine degli [[anni 1980|anni ottanta]] e primiall’inizio degli [[anni 1990|anni novanta]]. Tuttavia, quandoall’epoca i progettisti di CPU nonincontravano potevanolimitazioni farfisiche staree granditecnologiche: non era possibile integrare cache suidi chipgrandi edimensioni nondirettamente disponevanosul dichip, né c’era sufficiente larghezza di banda per implementaregestire altauna associativitàcache sullealtamente cacheassociativa al diposta fuori del chip deldal processore. FuronoPer provatesuperare variequeste difficoltà, furono esplorate diverse soluzioni: ilarchitetturali. Il processore [[Architettura MIPS|MIPS]] R8000, usavaad delleesempio, costoseutilizzava [[SRAM]] off-chip dedicate, chee includevanocostose, deidotate di comparatori di etichette e deidriver grandiad driveralte prestazioni, per implementare una cache associativa 4-way da 4MB4 MB. Il successivo MIPS R10000, usavainvece, dei chip ordinari diutilizzava SRAM comuni per conservare le etichette., L'accessoma allerichiedeva etichette,due incicli entrambedi leclock per accedere ai dati e verificare direzionil’etichetta, necessitavaa dicausa duedella cicli:maggiore percomplessità. Per ridurre la latenza di accesso, il R10000, perintrodusse ogniuna accessotecnica di predizione: al momento della richiesta di un dato, cercava di predireanticipare quale mododelle vie della cache sarebbeavrebbe statocontenuto quellol’informazione corretta, avviando in parallelo il controllo completo delle etichette. Questa strategia permetteva di accelerare l’accesso nella maggior parte dei casi, nonostante le limitazioni hardware del correttoperiodo.
 
== Cache hit ==
Cache colpita (in inglese cache hit) è il termine usato per indicare il successo del processore nel trovare un dato richiesto direttamente all'interno della memoria cache, senza dover accedere alla memoria principale (RAM), che è più lenta. Quando il dato cercato è presente in cache, si verifica un hit e il processore può accedere rapidamente all'informazione. Questo accesso può riguardare sia la lettura (cache read hit) sia la scrittura (cache write hit) del dato sulla linea di cache corrispondente.
Con "cache colpita" (in inglese ''cache hit'') ci si riferisce invece a un successo da parte del processore nel trovare l'indirizzo della posizione di memoria tra le varie etichette della cache che potrebbero contenerlo.
In caso di successo, il processore può leggere (''Hit di lettura di cache'') o scrivere (''Hit di scrittura di cache'') il dato sulla linea di cache.
 
In caso di ''Hithit di lettura'', il processore legge lail paroladato direttamente dalla cache, senzaevitando coinvolgerequalsiasi lacoinvolgimento della memoria centraleprincipale.
PerIn quantocaso riguardadi la ''Hithit di scrittura'', ciil siprocessore rimandascrive il dato nella cache secondo regole specifiche, all'approfondimentodescritte sullanella [[#Alcuni dettagli operativi|politica di scrittura della cache]].
 
== Traduzione degli indirizzi ==
La maggior parte delle CPU comunemente utilizzate implementanoimplementa un qualche tipo di sistema di [[memoria virtuale]]. InQuesto pratica,meccanismo consente a ogni programma chein giraesecuzione sulladi macchinavedere vede ilun proprio spazio di memoria separato, che contieneinclude solo il codice e i dati perrelativi ila soloquel programma. stesso,In inquesto manieramodo, semplificata. Ogniciascun programma mettepuò tuttooperare nelin propriomodo spazioindipendente, disenza memoriadoversi senzapreoccupare preoccuparsidella dimemoria quelloutilizzata che glidagli altri programmi fanno nei loro rispettivi spazi di memoria.
 
LaPer memoriarendere virtualepossibile richiedequesta cheseparazione, il processore traducadeve tradurre gli indirizzi virtuali generati dal programma in indirizzi fisici nella memoria principale. LaQuesta porzionetraduzione delè processoregestita cheda fauna questacomponente traduzionedel è conosciuta comeprocessore lachiamata ''[[memory management unit]]'' (MMU). La MMU può accedere velocementerapidamente allaalle tabellainformazioni di traduzionitraduzione attraversograzie ilal [[Translation Lookaside Buffer]] (TLB), una cache che èmemorizza unale cachecorrispondenze dipiù mappaturerecenti per ladella [[page table]] delgestita dal sistema operativo.
 
La traduzione degli indirizzi havirtuali in indirizzi fisici, svolta dalla [[memory management unit|MMU]], presenta tre caratteristiche importantifondamentali:
* Latenza: GeneralmenteIn genere, la MMU rendefornisce disponibile l'indirizzol’indirizzo fisico corrispondente pochi cicli di clock dopo che l'indirizzol’indirizzo virtuale è computatostato generato dal generatoreprocessore. Questo breve ritardo è necessario per completare la d'indirizzitraduzione.
* Aliasing: PiùÈ possibile che più indirizzi virtuali possonofacciano riferirsi ariferimento unoallo stesso indirizzo fisico. LaPer garantire la coerenza dei dati, la maggior parte dei processori garantisceassicura che tuttiogni glimodifica aggiornamentia al singoloun indirizzo fisico vengano eseguitiavvenga in ordinemodo ordinato. PerA permetteretal ciòfine, il processore deve assicurarsiimpedire che, inpiù ognicopie istante,dello esistastesso indato cachefisico unasiano solapresenti copiacontemporaneamente diin ogni indirizzo fisicocache.
* Granularità: Lo spazio degli indirizzi virtuali è suddiviso in blocchi chiamati pagine. PerAd esempio, uno spazio virtuale d'indirizzi di 4GB4 potrebbeGB può essere spezzettatosuddiviso in 10485761.048.576 pagine da 4Kb4 KB ciascuna, ognuna delle qualigestita può essere referenziata indipendentementeseparatamente. PerAlcuni ilsistemi supportosupportano dianche pagine di dimensioni variabili,; per ulteriori dettagli si vedereveda la voce [[memoria virtuale]].
 
UnaIn nota storica:origine, i primi sistemi condotati di [[memoria virtuale]] eranorisultavano moltopiuttosto lenti, perchépoiché richiedevanoper unogni accesso alla memoria era necessario consultare la page table, (residenteche inrisiedeva nella memoria) primaprincipale. diQuesto ognicomportava accessoun programmatodoppio accesso alla memoria. Senzaper cacheogni operazione, riducendo sensibilmente le prestazioni. Per ovviare a questo dimezzalimite, fu introdotta la velocitàTranslation diLookaside accessoBuffer alla([[Translation memoriaLookaside dellaBuffer|TLB]]), macchina.una Perpiccola questocache motivospecializzata che memorizza temporaneamente le mappature più recenti tra indirizzi virtuali e fisici. Di fatto, la TLB fu la prima cache hardware usatautilizzata in unnei computer, nonancor èprima stata unadelle cache didedicate ai dati o d'alle istruzioni, ma invece una TLB.
 
L'esistenzaLa distinzione tra d'indirizzi fisicivirtuali e virtualiindirizzi fisici ponesolleva la questione su quali didebbano essiessere utilizzareutilizzati per legli etichetteindici e glile indicietichette della cache. La motivazione di usareUtilizzare indirizzi virtuali èconsente launa maggiore velocità:, unaperché cacheevita di datiattendere conla indicitraduzione edda etichetteparte virtualidella esclude[[memory lamanagement unit|MMU]] dalle operazioniprima di caricamentoaccedere ealla usocache. deiQuesto datiè dallaparticolarmente memoria.importante Ilper ritardoridurre provocatola dallatenza di caricamento dei dati dalla memoria RAMprincipale, (''loadun latency'') èfattore crucialecritico per le prestazioni della CPU:. perPer questo motivo, la maggior parte dellemolte cache di primo livello 1(L1) sono progettate per essere indicizzate conusando indirizzi virtuali, permettendo alla MMU di ricercare nella TLBeseguire in parallelo conla ilricerca recuperonella deiTLB datie dallal'accesso cacheai delladati RAMpresenti nella cache.
 
L'indirizzamentoL’uso virtualedi indirizzi virtuali per l’accesso alla cache non è sempre la sceltasoluzione migliore:. introduce,Una addelle esempio,principali ildifficoltà problemaè rappresentata dal fenomeno degli ''alias virtuali'', cioèovvero la cachepossibilità potrebbeche immagazzinareuno instesso indirizzo fisico venga associato a più posizioniindirizzi ilvirtuali valorediversi. diIn unoquesti casi, la cache potrebbe conservare copie duplicate dello stesso indirizzodato fisico.in Ilposizioni costodistinte, percompromettendo la gestionecoerenza deglitra le informazioni. Gestire correttamente questi alias virtualidiventa crescepiù concomplesso laman dimensionemano dellache la cache e,cresce di dimensioni. Per comequesto risultatomotivo, la maggior parte delle cache di secondo livello 2(L2) e superioridi livello superiore sono progettate per essere indicizzate con indirizzi fisici, eliminando il problema alla radice e garantendo una gestione più efficiente e coerente dei dati in memoria.
 
NonL’impiego è comune, invece, l'uso deglidi indirizzi virtuali per le etichette (''della cache, noto come virtual tagging''), è piuttosto raro. SeIn molti casi, infatti, la ricerca nella [[Translation Lookaside Buffer|TLB]] finissesi conclude prima didell’accesso quellacompleto nellaalla memoria cache RAM, allorarendendo l'indirizzodisponibile l’indirizzo fisico sarebbe disponibile in tempo utile per ilconfrontarlo confrontocon dellele etichette. e,In quindiqueste situazioni, ilnon virtualè taggingnecessario nonutilizzare sarebbeetichette necessariovirtuali. CacheLe cache di grandi dimensioni, quindi,che tendonohanno atempi di accesso più lunghi, sono esseregeneralmente etichettateetichetate con indirizzi fisici (''physically tagged'') per garantire coerenza e solosemplicità nella gestione. Al contrario, le cache più piccole cachee conveloci, bassacome latenzaquelle sonodi virtuallyprimo taggedlivello, possono ancora utilizzare il virtual tagging per ridurre la latenza complessiva. Nelle CPUarchitetture più recenti, tuttavia, il virtual tagging è stato in parte sostituito daida ''vhints''meccanismi più avanzati, come descrittoi vhints, descritti più avanti, che consentono una gestione ancora più efficiente dell’indirizzamento e dell’accesso ai dati in bassocache.
 
=== Virtual indexing e virtual aliases ===
Il tipico modo con cui il processore garantisce che gli alias virtuali funzionino correttamente è ordinarli in maniera che, in ogni istante, solo un alias virtuale possa essere nella cache.
 
Il processore garantisce la corretta gestione degli alias virtuali ordinandoli in modo tale che, in ogni momento, nella cache possa essere presente una sola copia di un determinato indirizzo fisico corrispondente a uno degli alias virtuali.
Ogni volta che un nuovo valore è aggiunto alla cache, il processore cerca altri alias virtuali e li rimuove. Questa operazione avviene solo in caso di un fallimento della cache. Nessun lavoro particolare è necessario durante un cache hit, il che aiuta a mantenere il più rapido possibile il percorso veloce nella cache.
 
Ogni volta che un nuovo dato viene inserito nella cache, il processore verifica se esistono altri alias virtuali corrispondenti e, se li trova, li elimina per evitare duplicazioni. Questa operazione avviene solo in caso di cache miss (mancato reperimento del dato). Durante un cache hit (accesso riuscito), invece, non è necessario alcun controllo aggiuntivo, permettendo così di mantenere il percorso rapido e efficiente all’interno della cache.
La via più immediata per trovare gli alias è di mapparli tutti alla stessa area della cache. Questo succede, per esempio, se il TLB ha pagine da 4KB, e la cache è direct-mapped e a 4KB o meno.
 
Il modo più semplice per individuare gli alias virtuali è far sì che tutti gli indirizzi virtuali che corrispondono allo stesso indirizzo fisico siano mappati nella stessa area della cache. Questo avviene, ad esempio, quando il TLB gestisce pagine di 4 KB e la cache è direct-mapped con una dimensione pari o inferiore a 4 KB.
Le moderne cache di primo livello sono molto più grandi di 4KB, ma le pagine di memoria virtuale sono rimaste della stessa dimensione. Se, per esempio, la cache è da 16KB e indicizzata virtualmente, ogni indirizzo fisico può essere indirizzato da 4 diverse posizioni della cache, per altrettanti indirizzi virtuali. Se la cache fallisce, tutte e quattro le posizioni devono essere controllate per verificare se i loro indirizzi fisici corrispondenti effettivamente coincidono con l'indirizzo fisico dell'accesso che ha generato il fallimento.
 
Le moderne cache di primo livello sono molto più grandi di 4 KB, mentre la dimensione delle pagine di memoria virtuale è rimasta generalmente invariata. Ad esempio, se la cache è di 16 KB e indicizzata con indirizzi virtuali, uno stesso indirizzo fisico può corrispondere a quattro diverse posizioni nella cache, a causa di differenti indirizzi virtuali associati. In caso di cache miss, il processore deve controllare tutte e quattro queste posizioni per verificare se gli indirizzi fisici corrispondono realmente a quello richiesto, prima di procedere con l’accesso alla memoria principale.
Questi controlli sono gli stessi che una cache ''set associative'' usa per selezionare una particolare corrispondenza. Quindi se un cache indicizzata virtualmente da 16KB, 4-way set associative, viene usata con pagine di memoria virtuale da 4KB, non è necessario alcun lavoro aggiuntivo per eliminare gli alias virtuali in caso di cache miss, in quanto i controlli sono già stati effettuati durante il controllo della cache.
 
I controlli necessari per individuare e gestire gli alias virtuali sono simili a quelli che una cache set-associativa utilizza per trovare una corrispondenza specifica. Per esempio, in una cache indicizzata virtualmente da 16 KB con una configurazione 4-way set-associativa e pagine di memoria virtuale da 4 KB, non è richiesto alcun intervento aggiuntivo per eliminare gli alias virtuali in caso di cache miss. Questo perché i controlli per confrontare gli indirizzi fisici sono già eseguiti durante la ricerca nella cache, evitando così duplicazioni e mantenendo l’efficienza.
Usiamo ancora un [[Advanced Micro Devices|AMD]] [[Athlon Classic|Athlon]] come esempio: esso ha una cache dati di primo livello da 64KB, con pagine da 4KB, set associative 2-way. Quando la cache dati di primo livello risente di un fallimento, 2 dei 16 (=64KB/4KB) possibili alias virtuali sono già stati controllati e sono necessari sette ulteriori cicli del circuito di controllo delle etichette per completare l'eliminazione degli ulteriori alias virtuali.
 
Prendendo come esempio un [[Advanced Micro Devices|AMD]] [[Athlon Classic|Athlon]], questo processore dispone di una cache dati di primo livello da 64 KB, con pagine di memoria virtuale da 4 KB e una configurazione 2-way set associative. Quando la cache dati di primo livello subisce un cache miss, due dei sedici possibili alias virtuali (dato che 64 KB diviso 4 KB fa 16) sono già stati controllati. Per completare l’eliminazione degli eventuali altri alias virtuali, sono necessari circa sette cicli aggiuntivi nel circuito di controllo delle etichette.
 
=== Virtual tags and vhints ===
È possibile utilizzare anche l'etichettatural’etichettatura virtuale (Virtualvirtual tagging). Il grandeprincipale vantaggio deldi virtual tag èquesto chemetodo, persoprattutto lenelle cache associative, permettonoè lache corrispondenzapermette delledi confrontare le etichette prima che avvenga la traduzione dadall’indirizzo virtuale a fisicaquello siafisico, fatta.accelerando Comunque:così l’accesso.
Tuttavia, ci sono alcune complessità da considerare:
* I controlli di coerenza e la rimozione delle voci nella cache si basano sugli indirizzi fisici. Perciò, l’hardware deve disporre di un meccanismo per convertire l’indirizzo fisico in una posizione della cache. Questo comporta che la cache memorizzi sia le etichette virtuali sia quelle fisiche. Al contrario, una cache con etichettatura fisica (physically tagged) gestisce solo etichette fisiche, semplificando il progetto.
* Quando una traduzione dall’indirizzo virtuale a quello fisico viene rimossa dalla [[Translation Lookaside Buffer|TLB]], le informazioni corrispondenti nella cache devono essere invalidate (cioè svuotate o aggiornate). Inoltre, se la cache contiene dati relativi a pagine non più mappate dalla TLB, questi dati devono essere rimossi quando cambiano i permessi di accesso a tali pagine nella page table.
 
Il sistema operativo può permettere che più alias virtuali dello stesso indirizzo fisico siano presenti contemporaneamente nella cache, tramite una tecnica chiamata page coloring, che sarà descritta più avanti. Alcuni processori RISC, come quelli delle famiglie SPARC e RS/6000, hanno adottato questo approccio. Tuttavia, recentemente questa pratica è stata meno utilizzata, perché il costo hardware per rilevare e rimuovere gli alias virtuali si è ridotto, mentre la complessità e l’impatto sulle prestazioni del software necessario per implementare un page coloring preciso sono aumentati.
* Controlli di coerenza e rimozione presentano un indirizzo fisico per azione. L'hardware deve avere qualche metodo per convertire l'indirizzo fisico in un indirizzo della cache, generalmente immagazzinando etichette fisiche così come le etichette virtuali. Per confronto, una cache etichettata fisicamente non necessita di mantenere etichette virtuali, il che è più semplice.
* Quando un riferimento da virtuale a fisico viene eliminato dalla TLB, le informazioni della cache con quegli indirizzi virtuali dovranno essere svuotati in qualche maniera. Se le informazioni della cache sono permesse su pagine non mappate dalla TLB, allora queste informazioni dovranno essere svuotate quando i diritti di accesso su queste pagine cambiano nella page table.
 
È utile distinguere due funzioni svolte dall’etichettatura in una cache associativa:
È possibile per il sistema operativo assicurarsi che più virtual aliases siano contemporaneamente residenti nella cache. Il sistema operativo garantisce questo sforzando il ''page coloring'' che viene descritto più avanti. Alcuni recenti processori RISC (SPARC, RS/6000) hanno preso questo approccio. Non è stato usato di recente, siccome il costo dell'hardware per scoprire e rimuovere gli alias virtuali si è abbassato mentre la complessità e il prezzo prestazionale del software per un perfetto page coloring si è alzato.
* La prima serve a scegliere quale linea del set selezionare (modalità di selezione).
* La seconda serve a verificare se l’accesso alla cache è un successo o un fallimento (hit o miss).
* La seconda funzione deve sempre essere accurata, mentre per la prima è possibile accettare qualche errore occasionale (una “predizione” non sempre corretta), senza compromettere il funzionamento complessivo della cache.
 
Alcuni processori, come quelli recenti della famiglia SPARC, utilizzano cache che combinano sia etichette virtuali sia etichette fisiche. In questo tipo di cache, le etichette virtuali sono impiegate per selezionare il modo (ossia la linea o il blocco da verificare), mentre le etichette fisiche servono a determinare se l’accesso è un successo (cache hit) o un fallimento (cache miss). Questa soluzione unisce il vantaggio di una bassa latenza tipica delle cache con etichette virtuali alla semplicità di gestione del software garantita dalle cache con etichette fisiche. Tuttavia, comporta un costo maggiore dovuto alla necessità di mantenere etichette duplicate.Durante i casi di fallimento della cache, i modi alternativi della linea devono essere controllati per eventuali alias virtuali e, se necessario, le corrispondenze errate devono essere eliminate per mantenere la coerenza.
Potrebbe essere utile distinguere le due funzioni di etichettatura in una cache associativa: vengono utilizzate per determinare quale modalità del set d'informazioni da selezionare, e vengono utilizzate per determinare se la cache fallisce o no. La seconda funzione deve essere sempre corretta, ma è permesso alla prima funzione d'indovinare, e avere la risposta sbaglia occasionalmente.
 
L’area di memoria aggiuntiva (e la relativa latenza) associata all’uso di etichette virtuali può essere mitigata impiegando dei virtual hints invece delle etichette virtuali complete. Questi hints rappresentano un sottoinsieme, o un hash, dell’etichetta virtuale e vengono utilizzati per selezionare il modo della cache da cui leggere i dati e la relativa etichetta fisica. In una cache con etichette virtuali, può verificarsi una corrispondenza tra virtual hints ma non tra etichette fisiche: in tal caso, l’informazione nella cache associata all’hint coincidente deve essere rimossa, affinché gli accessi successivi allo stesso indirizzo trovino una sola corrispondenza. Poiché i virtual hints contengono meno bit rispetto alle etichette virtuali complete, distinguono meno efficacemente gli indirizzi. Di conseguenza, una cache basata su virtual hints tende a soffrire più frequentemente di mancanze dovute a conflitti (conflict misses) rispetto a una cache con etichette virtuali complete.
Alcuni processori (Ad esempio recenti SPARC) hanno le cache sia con etichette virtuali che fisiche. Le etichette virtuali vengono utilizzate per la selezione del modo, e le etichette fisiche sono utilizzate per determinare il centro o il fallimento. Questo tipo di cache favorisce il vantaggio della latenza di una cache a etichette virtuali, e la semplice interfaccia software di una cache a etichette fisiche. Supporta il costo aggiunto di etichette duplicate, comunque. Anche durante i processi di fallimento, i modi alternati della linea della cache devono essere controllati per virtual alias e per ogni corrispondenza rimossa.
 
Un esempio estremo di riduzione dei virtual hints si osserva nei processori Pentium 4 (core Willamette e Northwood). In questi modelli, l’hint virtuale è ridotto a soli 2 bit, mentre la cache è organizzata come una 4-way set associative. Il processore implementa una semplice permutazione tra gli indirizzi virtuali e gli indirizzi della cache, tale da rendere superfluo l’uso di un Content-Addressable Memory (CAM) per selezionare il modo corretto da cui recuperare i dati. Questo approccio consente una selezione rapida del blocco in cache, pur mantenendo i vantaggi dell’associatività.
L'area extra (e qualche latenza) può essere mitigata mantenendo ''virtual hints'' con ogni informazione della cache invece che con etichette virtuali. Questi hints sono un sottoinsieme o hash di un'etichetta virtuale, e vengono utilizzati per selezionare il modo della cache tramite cui prelevare un dato e un'etichetta fisica. Con una cache virtually tagged, ci potrebbe essere una corrispondenza di virtual hint ma una non corrispondenza di etichetta fisica, in questo caso la informazione nella cache con la corrispondenza dell'hint deve essere rimossa cosicché accessi alla cache dopo il riempimento della cache in questo indirizzo avranno solamente una sola corrispondenza di hint. Siccome gli hint hanno minori bit delle etichette virtuali distinguerli uno dall'altro, una cache con hint virtuali soffre di più mancanze di conflitti di una cache a etichette virtuali.
 
Forse la riduzione finale di hint virtuali può essere trovata nel Pentium 4 (Willamette and Northwood cores). In questi processori, l'hint virtuale è effettivamente di soli 2 bit, e la cache è 4-way associative. In effetti, l'hardware mantiene una semplice permutazione da indirizzi virtuali a indirizzi di cache, cosicché nessun CAM sia necessario per selezionare quello giusto dei quattro modi di recupero.
 
=== Page coloring ===
Cache indicizzate fisicamente larghe (solitamente cache secondarie) riscontrano un problema: il sistema operativo piuttosto che le applicazioni controlla quali pagine collidono vicendevolmente nella cache. Differenze nell'allocazione delle pagine da un programma portano al prossimo livello di differenze nei percorsi di collisione della cache, i quali possono portare a differenze molto larghe nelle prestazioni dei programmi. Queste differenze possono far diventare molto difficile ottenere un consistente e ripetibile tempo di benchmark per i programmi in esecuzione, che porta ingegneri pagati e sconsolati a richiedere che gli autori del sistema operativo risolvano il problema.
 
Le cache fisicamente indicizzate di grandi dimensioni (tipicamente le cache di secondo livello) possono presentare un problema noto: il sistema operativo, anziché le applicazioni, determina quali pagine di memoria possano collidere all’interno della cache. Differenze nell’allocazione delle pagine tra diverse esecuzioni dello stesso programma possono modificare i pattern di collisione nella cache, con conseguenti variazioni significative nelle prestazioni. Tali variazioni rendono difficile ottenere misurazioni di benchmark stabili e riproducibili, portando spesso gli ingegneri a sollecitare interventi da parte degli sviluppatori del sistema operativo per mitigare il problema.
Per capire il problema, consideriamo una CPU con 1MB di cache di livello-2 direct-mapped indicizzata fisicamente e 4KB di pagine di memoria virtuale. Pagine fisiche in sequenza si mappano in posizioni in sequenza nella cache fino a che dopo 256 pagine il percorso torna su se stesso<!-- help -->. Possiamo etichettare ogni pagina fisica con un colore da 0-255 per denotare dove nella cache può andare. Posizioni all'interno di pagine fisiche con colori differenti non possono entrare in conflitto nella cache.
 
Per comprendere il problema, si consideri una CPU dotata di una cache di secondo livello (L2) da 1 MB, direct-mapped e indicizzata fisicamente, in combinazione con una memoria virtuale suddivisa in pagine da 4 KB. In questo scenario, 256 pagine fisiche consecutive si mappano su posizioni consecutive nella cache, fino a completare un ciclo e tornare alla posizione iniziale. Ogni pagina fisica può quindi essere associata a un "colore", compreso tra 0 e 255, che rappresenta la sua posizione potenziale nella cache. Di conseguenza, due pagine con colori diversi non si sovrappongono nella cache, mentre pagine con lo stesso colore possono entrare in conflitto, causando evizioni e influenzando le prestazioni del sistema.
Un programmatore che voglia usare al massimo l'uso della cache potrebbe arrangiare i suoi accessi del programma cosicché solo 1MB di data necessiti di essere messo in cache per volta, tutto questo evitando fallimenti di capacità. Ma dovrebbe anche assicurarsi che gli accessi non abbiano fallimenti di conflitto. Un modo per pensare a questo problema è di suddividere le pagine virtuali che utilizza il programma e assegnare a loro colori virtuali nello stesso modo come colori fisici erano assegnati a pagine fisiche precedentemente. Il programmatore può poi arrangiare gli accessi del suo codice in modo che due pagine con lo stesso colore virtuale non siano in uso nello stesso momento. C'è una distesa letteratura su queste ottimizzazioni (per esempio. [[Loop nest optimization]]), proveniente soprattutto dalla comunità [[High Performance Computing]] (HPC).
 
Un programmatore che desideri sfruttare al meglio la [[cache]] del processore può organizzare gli accessi alla [[memoria]] del programma in modo da ridurre al minimo i [[cache miss]] di tipo capacitivo, ovvero quei casi in cui la quantità di dati supera lo spazio disponibile nella cache. Una strategia efficace è limitare l’uso della memoria attiva a una dimensione contenuta (ad esempio 1 MB) per volta, così che l'intero set di dati possa essere contenuto nella cache senza causare sostituzioni continue. Tuttavia, anche mantenendo bassa la quantità di dati, bisogna evitare i [[cache miss]] di conflitto, che si verificano quando diverse porzioni di memoria competono per le stesse linee della cache a causa della struttura della mappatura. Un modo per affrontare questo problema consiste nell’assegnare colori virtuali alle pagine di memoria usate dal programma, in modo simile a come vengono assegnati i colori fisici alle pagine fisiche in alcuni sistemi. Il programmatore può così pianificare l'accesso alla memoria affinché due pagine con lo stesso colore virtuale non vengano utilizzate nello stesso momento, evitando così che si mappino sulla stessa porzione della cache.
Il concetto è che mentre tutte le pagine in uso in un determinato momento, potrebbero avere differenti colori virtuali, alcune potrebbero avere lo stesso colore fisico, Infatti, se il sistema operativo assegna pagine fisiche a pagine virtuali in modo casuale e uniforme, è molto probabile che alcune pagine abbiano lo stesso colore fisico, e quindi posizioni da queste pagine coincidano nella cache (questo è il [[Birthday paradox]]).
 
Esiste un'ampia letteratura su questo tipo di ottimizzazioni, in particolare nell’ambito del [[High Performance Computing]] (HPC), dove sono frequentemente utilizzate tecniche come la Loop nest optimization per migliorare l'efficienza dell’accesso ai dati e sfruttare meglio la gerarchia di memoria.
La soluzione sta nel fare in modo che il sistema operativo tenti di assegnare pagine fisiche colorate diversamente a differenti colori virtuali, una tecnica chiamata ''page coloring''. Sebbene la mappatura attuale da colori virtuali a fisici sia irrilevante per le prestazioni del sistema, mappature dispari sono difficili da tracciare e hanno piccoli benefici, quindi la maggior parte degli approcci alla colorazione delle pagine tenta semplicemente di tenere pagine fisiche e virtuali colorate nello stesso modo.
 
Va infine considerato che, anche se le pagine virtuali attive sono diversificate, il [[sistema operativo]] potrebbe assegnare loro pagine fisiche in modo casuale o uniforme. Questo comporta un’elevata probabilità che due pagine abbiano lo stesso colore fisico e quindi occupino la stessa posizione nella cache, causando conflitti. Questo effetto statistico è descritto dal Birthday paradox.
Se il sistema operativo può garantire che ogni pagina fisica si riferisca a un solo colore virtuale, allora non vi sono virtual alias, e il processore può usare cache virtually indexed senza la necessità di controlli su extra virtual alias durante la gestione del fallimento. Alternativamente il sistema operativo può svuotare una pagina dalla cache quantunque cambi da un colore virtuale a un altro. Come menzionato prima, questo approccio fu usato da qualche recente progettazione SPARC e RC/6000.
 
La soluzione a questo problema consiste nel fare in modo che il [[sistema operativo]] tenti di assegnare pagine fisiche con colori fisici differenti a colori virtuali differenti, una tecnica nota come ''page coloring''. Questa tecnica mira a ridurre i [[cache miss]] di conflitto, migliorando l’efficienza nell’uso della [[cache]]. Sebbene la corrispondenza esatta tra colori virtuali e fisici possa sembrare poco rilevante per le prestazioni complessive del sistema, mappature troppo irregolari o non uniformi risultano difficili da gestire e offrono benefici limitati. Per questa ragione, la maggior parte degli approcci alla colorazione delle pagine cerca semplicemente di mantenere una coerenza tra il colore fisico e quello virtuale, assegnando pagine fisiche dello stesso colore a pagine virtuali corrispondenti.
 
Se il sistema operativo riesce a garantire che ogni pagina fisica venga associata a un solo colore virtuale, si evitano i cosiddetti ''virtual alias'', ovvero situazioni in cui più indirizzi virtuali puntano alla stessa locazione fisica. In questo caso, il [[processore]] può utilizzare una cache con virtual indexing senza dover eseguire controlli aggiuntivi per gestire alias multipli durante la rilevazione di cache miss. In alternativa, il sistema operativo può forzare lo svuotamento (''flush'') di una pagina dalla cache ogni volta che essa cambia da un colore virtuale a un altro. Questo metodo è stato utilizzato in alcune architetture recenti, come quelle basate su [[SPARC]] o [[IBM RS/6000]].
 
== Gerarchia delle cache in un processore moderno ==
 
I processori moderni dispongono sul chip di cache multiple con cui interagire. Due motivi, in particolare, hanno portato allo sviluppo della attuale gerarchia delle cache.
I [[processori]] moderni adottano una gerarchia di [[cache]] multilivello (tipicamente L1, L2 e L3) per ridurre la latenza nell’accesso alla memoria principale e migliorare le prestazioni complessive del sistema. Questa struttura nasce da due necessità fondamentali: la crescente distanza in termini di velocità tra CPU e RAM, e il bisogno di gestire in modo efficiente l’accesso concorrente nei sistemi multicore. La cache L1 è la più piccola e veloce, situata direttamente all’interno di ciascun [[core]] del processore; viene spesso divisa in una sezione per i dati e una per le istruzioni. La cache L2 è più capiente e leggermente più lenta, mentre la cache L3, ancora più ampia, è generalmente condivisa tra tutti i core del chip. Questa gerarchia consente di conservare localmente i dati usati più frequentemente (''data locality''), riducendo il numero di accessi alla RAM e migliorando l’efficienza energetica. Nei sistemi più recenti, l’organizzazione delle cache è progettata per adattarsi a carichi di lavoro eterogenei, grazie a tecniche come il page coloring o l’indicizzazione virtuale delle cache, che riducono i conflitti e migliorano l’utilizzo dello spazio disponibile. In contesti ad alte prestazioni, come l’[[High Performance Computing]] o i sistemi con architettura chiplet, la gerarchia delle cache è ottimizzata anche per gestire la comunicazione tra core e acceleratori, attraverso reti su chip (Network on Chip) e cache condivise a bassa latenza.
La progettazione moderna mira a mantenere un equilibrio tra velocità, capacità e coerenza, con meccanismi intelligenti di prefetching, gestione degli alias virtuali e strategie di coerenza tra cache multilivello.
 
=== Cache specializzate ===
 
Il primo motivo è che CPU con pipeline accedono alla memoria da molteplici punti nella pipeline: recupero delle istruzioni, traduzione indirizzi da virtuali a fisici, e recupero dei dati. La naturale implementazione è di utilizzare differenti cache fisiche per ognuno di questi punti, cosicché nessuna risorsa fisica debba essere programmata per servire due punti nella pipeline. Sebbene la pipeline finisca naturalmente con almeno tre cache separate (istruzioni, TLB, e data), ognuna è specializzata in un ruolo particolare.
Il primo motivo che ha portato allo sviluppo di una gerarchia di [[cache]] nei processori moderni è legato al funzionamento delle [[CPU]] con [[pipeline]]. In queste architetture, la memoria viene interrogata in più fasi della pipeline, durante il recupero delle istruzioni, nella traduzione degli indirizzi da virtuali a fisici e nel recupero dei dati. Per evitare conflitti e colli di bottiglia, la soluzione più efficiente consiste nell'utilizzare cache fisiche separate per ciascuno di questi punti, così che nessuna risorsa debba servire più operazioni contemporaneamente. Questo approccio consente un'elevata parallelizzazione delle fasi di esecuzione, migliorando le prestazioni complessive del processore.
Di conseguenza, la pipeline di una CPU moderna tende a includere almeno tre cache distinte: una cache per le istruzioni (instruction cache), una per i [[dati]] (data cache) e una per la [[Translation Lookaside Buffer]] (TLB), utilizzata per la traduzione rapida degli indirizzi di memoria. Ciascuna di queste cache è specializzata in un compito specifico e ottimizzata per il tipo di accesso che deve gestire. Questa suddivisione consente di ridurre la latenza, aumentare la velocità di esecuzione e garantire un accesso più efficiente alla memoria principale all'interno di architetture sempre più complesse e multi-thread.
 
=== Victim cache ===
 
Una victim cache è una cache utilizzata per mantenere blocchi rimossi dalla cache della CPU a causa di un conflict miss o capacity miss. La victim cache è situata tra la cache primaria e la memoria sottostante, e mantiene solamente i blocchi rimossi dopo un miss. Questa tecnica è utilizzata per ridurre la penalità in cui si incorre per un fallimento della cache, perché può accadere che i dati che sono nella victim cache vengano richiesti qualche tempo dopo, e allora invece di dichiarare un miss, e andare in memoria a recuperare questi dati, si controlla la victim cache e si utilizzano i dati che sono ancora dentro di essa.
 
Riga 146 ⟶ 181:
 
=== Trace cache ===
Uno dei più estremi esempi di specializzazione della cache è quello della trace cache utilizzata nei microprocessori [[Pentium 4]]. Una trace cache è un meccanismo per aumentare il [[fetch bandwidth]] d'istruzioni immagazzinando tracce d'istruzioni che sono già state immagazzinate. Il meccanismo fu per la prima volta proposta da [[Eric Rotenberg]], [[Steve Bennett]] e Jim Smith nel loro articolo del 1996: "''Trace Cache: a Low Latency Approach to High Bandwidth Instruction Fetching.''"
 
Uno degli esempi più avanzati di specializzazione della [[cache]] è rappresentato dalla trace cache, utilizzata nei microprocessori [[Pentium 4]]. Si tratta di un meccanismo progettato per aumentare la [[larghezza di banda]] nel recupero delle istruzioni (''fetch''), memorizzando sequenze di istruzioni già decodificate, pronte per un rapido riutilizzo.
Una trace cache immagazzina le istruzioni anche dopo che esse siano state eseguite, o come vengono ritirate. Generalmente, le istruzioni vengono aggiunte alle trace cache in gruppi che rappresentano sia blocchi individuali di base che tracce d'istruzioni dinamiche. Un blocco base consiste in un gruppo d'istruzioni non-branch (Non suddivise) che finiscono con una ramificazione. Una traccia dinamica ("trace path" o "traccia del percorso") consistono nelle solo istruzioni di cui il risultato viene effettivamente utilizzato, ed elimina le istruzioni seguenti che prendono ramificazioni (Siccome non sono eseguite); una traccia dinamica può essere il concatenamento di multipli di blocchi base. Questo permette all'unità di recupero delle istruzioni di recuperare parecchi blocchi basici, senza la preoccupazioni riguardante la ramificazione nel flusso dell'esecuzione.
Il concetto di trace cache è stato introdotto da Eric Rotenberg, Steve Bennett e [[Jim Smith]] nel loro articolo del 1996: ''Trace Cache: a Low Latency Approach to High Bandwidth Instruction Fetching''.
 
A differenza delle cache tradizionali, la trace cache può contenere istruzioni già eseguite o pronte per l'esecuzione, organizzate in gruppi detti tracce. Queste possono rappresentare blocchi di base (cioè sequenze di istruzioni consecutive senza salti, che terminano con una ramificazione) oppure tracce dinamiche. Le tracce dinamiche sono percorsi di esecuzione effettivamente seguiti dal programma: includono solo le istruzioni realmente eseguite e omettono quelle saltate a causa di ramificazioni condizionali. Questa struttura consente al processore di recuperare rapidamente istruzioni da più blocchi consecutivi, evitando l’interruzione causata da salti condizionali o diramazioni nel flusso del programma. In questo modo, la trace cache contribuisce a migliorare l’efficienza del recupero delle istruzioni e a ridurre la latenza nell’esecuzione.
 
Le tracce memorizzate nella trace cache sono identificate tramite il [[program counter]] della prima istruzione e da un insieme di predizioni sulle ramificazioni. Questo consente di conservare diverse versioni di tracce che iniziano dallo stesso indirizzo, ognuna corrispondente a differenti esiti delle ramificazioni condizionali.
Le linee di traccia vengono immagazzinate nella trace cache in base al [[program counter]] della prima istruzione nella traccia e un set di predizioni di ramificazioni. Questo permette l'immagazzinamento di differenti tracce di percorsi che iniziano con lo stesso indirizzo, ognuna delle quali rappresenta differenti risultati di ramificazione. Nello stage dell'immagazzinamento delle istruzioni di un'[[Instruction pipeline]], il program counter corrente insieme a un set di predizioni di ramificazione viene controllato nella trace cache per un hit. Se un hit avviene, una linea di trace viene fornita per recuperare quale non deve andare in una cache regolare o in memoria per queste istruzioni. La trace cache continua ad alimentare la fetch unit fino a che la linea di traccia finisce o fino a che vi sia una [[misprediction]] nella pipeline. Se c'è un fallimento, una nuova traccia inizia a essere creata. Il vantaggio rispetto alle normali cache per il codice è che non vengono mantenute in cache tutte le istruzioni successive a un branch che sia incondizionato o predetto come non seguito: il risultato è che non si formano "bolle" di codice non utilizzato che sprecano spazio di memoria della cache.
Durante la fase di prelievo delle istruzioni nell'instruction pipeline, il processore controlla nella trace cache se esiste una traccia corrispondente al program counter attuale e alle predizioni di salto. Se la ricerca ha esito positivo (hit), le istruzioni vengono fornite direttamente dalla trace cache, evitando di doverle recuperare dalla memoria o da una cache convenzionale. La trace cache continua ad alimentare la fetch unit fino al termine della traccia o fino a una misprediction, cioè quando la previsione su una ramificazione si rivela errata. In quel caso, si avvia la costruzione di una nuova traccia.
Rispetto alle cache tradizionali, la trace cache offre un vantaggio importante: non conserva istruzioni che seguono una ramificazione non presa o un salto incondizionato. Questo evita di occupare spazio con istruzioni che non verranno eseguite, migliorando l'efficienza complessiva.
Nei processori come l’[[Intel Pentium 4]], le trace cache vengono utilizzate anche per memorizzare micro-operazioni già decodificate (derivate da istruzioni complesse dell’architettura x86), permettendo di evitarne la decodifica nelle successive esecuzioni.
 
L'idea alla base della trace cache nasce dall’esigenza di ottimizzare le prestazioni dei processori [[CISC]] che, internamente, traducono le istruzioni complesse in micro-operazioni più semplici simili a quelle di un'architettura [[Reduced instruction set computer|RISC]], come avviene nel [[Pentium 4]]. In questi casi, la fase di decodifica delle istruzioni risulta particolarmente onerosa in termini di tempo e risorse. Utilizzando una trace cache, si evita di ripetere la decodifica per istruzioni già eseguite, riducendo così il carico sul processore e migliorando l’efficienza.
Le Trace cache vengono anche impiegate in processori quali l'[[Pentium 4|Intel Pentium 4]] per immagazzinare micro operazioni già decodificate, o traduzioni di complesse istruzioni x86, cosicché alla successiva richiesta della stessa istruzione, non debba essere decodificata.
 
Tuttavia, l’impiego della trace cache ha mostrato alcuni limiti che ne hanno ridotto l’adozione nelle architetture più recenti. In molti casi, le istruzioni CISC vengono tradotte in una sola micro-operazione RISC in un singolo ciclo di clock. Le istruzioni più complesse, che richiedono più cicli e generano più micro-operazioni, sono relativamente rare. Questo riduce il vantaggio prestazionale della trace cache rispetto a una cache tradizionale.
L'idea che sta alla base della trace cache è che nei processori [[CISC]] che internamente utilizzano istruzioni [[Reduced instruction set computer|RISC]], come il Pentium 4, la decodifica delle istruzioni è un'operazione estremamente onerosa, e il suo risultato dovrebbe essere sfruttato al meglio. Utilizzare una trace cache in luogo di una normale cache ha proprio questo vantaggio: non dover decodificare un'istruzione già incontrata durante l'esecuzione di un programma.
 
UltimamenteUn laulteriore trace cache non gode di molti favori a causa di alcuni difetti. Il primolimite è chelegato moltealla istruzionirappresentazione RISC sono tradotte in una singola istruzione CISC in un solo ciclo di clock, e ledelle istruzioni che necessitano di più cicli di clock per essere tradotte in più istruzioni di tipo RISC sono relativamente poche e poco frequenti, per cui il vantaggio effettivo della trace cache è limitato. ANelle questo si aggiunge il fatto che, nelarchitetture casocome dell'architetturaquella di Intel, le istruzioni di tipo CISC hanno lunghezza variabile in genere tra(da 1 ea 6 byte, ovvero (tra gli 8 e i 48 bit), mentre tutte le istruzionimicro-operazioni RISC utilizzate internamentederivate hanno lunghezza fissa di(ad esempio, 118 bit nel Pentium 4). QuindiDi conseguenza, a parità di dimensionispazio occupato in memoria, una trace cache contieneriesce a moltecontenere meno istruzioni dirispetto a una cache normale.convenzionale, limitandone ulteriormente l’efficacia.
 
Vedi il testo {{cita testo|url=http://citeseer.ist.psu.edu/rotenberg96trace.html|titolo=Smith, Rotenberg and Bennett 's paper|accesso=30 novembre 2019|dataarchivio=3 aprile 2008|urlarchivio=https://web.archive.org/web/20080403043445/http://citeseer.ist.psu.edu/rotenberg96trace.html|urlmorto=sì}} Inin [[CiteseerCiteSeer]].
 
== Cache multilivello ==
Il secondo motivo è il fondamentale compromesso tra la cache latency e lo hit rate. Le cache più grandi sono più lente e hanno migliori hit rate. Per migliorare questo ''tradeoff'', molti sistemi utilizzano livelli multipli di cache, con cache piccole e veloci che si appoggiano a cache più grandi e più lente. Siccome la differenza di latenza tra la memoria principale e le cache più veloci è diventata più grande, alcuni processori hanno cominciato a utilizzare anche tre livelli di cache nel chip. Per esempio nel 2003, Itanium II iniziò a essere fornito con una cache sul chip unificata di livello 3 di 6MB. L'IBM Power 4 series ha una cache di livello 3 a 256 MB fuori dal chip, condivisa attraverso parecchi processori.
 
Le [[cache]] multilivello introducono un nuovo modello nella gestione dei dati. In molti processori moderni, come quelli della famiglia [[Intel Core]] (dalla generazione [[Sandy Bridge]] in poi) e alcuni [[Architettura ARM|ARM]] avanzati, i dati presenti nella cache [[L1]] sono sempre contenuti anche nella cache [[L2]]. Questo tipo di organizzazione è detta inclusive.Al contrario, altri processori, come quelli della serie AMD Ryzen, adottano cache exclusive, dove ogni dato risiede al massimo in una sola delle due cache, o nella [[L1]] o nella [[L2]], ma mai in entrambe contemporaneamente.
Le cache multilivello generalmente operano controllando dapprima le cache a livello 1; se avviene un hit, il processore procede ad alta velocità. Se la cache più piccola “fallisce”, allora viene controllata quella più grande e così via, fino a dover accedere alla memoria principale.
 
Le cache multi livello introducono un nuovo modello decisionale. Per esempio, in alcuni processori (come gli [[Intel]] [[Pentium II|Pentium 2]], [[Pentium III|3]], e [[Pentium 4|4]], così come in molti [[RISC]]), i dati nella cache L1 possono essere anche in quella L2. Queste cache vengono denominato inclusive. Altri processori (come l'[[Advanced Micro Devices|AMD]] [[Athlon Classic|Athlon]]) hanno cache exclusive in cui è garantito che i dati siano al massimo in una delle cache L1 o L2.
 
Il vantaggio delle cache esclusive è che possono memorizzare più dati. Questo vantaggio diventa più evidente con cache di dimensioni maggiori, sebbene questa caratteristica non sia presente nelle implementazioni [[Architettura x86|Intel x86]]. D'altra parte, un vantaggio delle cache inclusive è che quando dispositivi esterni o altri processori in un sistema [[multiprocessore]] vogliono rimuovere una linea di cache dal processore, devono far controllare al processore solo la cache L2. Nelle gerarchie di cache che non utilizzano l'inclusione, è necessario controllare anche le cache L1. Inoltre, esiste una correlazione tra l'associatività delle cache L1 e L2: se le cache L2 non hanno almeno tanti modi quanti le cache L1 complessivamente, l'effettiva associatività delle cache L1 risulta limitata.
 
Il vantaggio delle cache exclusive è che possono immagazzinare una quantità maggiore di dati totali, un beneficio che diventa più evidente all’aumentare delle dimensioni delle cache. Tuttavia, questa caratteristica non è comune nelle implementazioni [[x86]] di [[Intel]]. D’altra parte, il vantaggio principale delle cache inclusive è che, in un sistema [[multiprocessore]], quando un dispositivo esterno o un altro processore deve invalidare una linea di cache, può controllare soltanto la cache L2 per decidere se eliminarla, senza dover verificare anche la L1. In sistemi senza inclusione, invece, è necessario controllare entrambe le cache L1 e L2, rendendo il processo più complesso.
Inoltre, esiste una relazione tra l’[[associatività]] delle cache L1 e L2: affinché l’associatività effettiva della cache L1 non venga limitata, la cache L2 deve avere almeno un numero di modi (set associativi) pari o superiore al totale delle cache L1 nel sistema.
Un altro vantaggio delle cache inclusive è che le cache più grandi possono usare linee di cache più grandi, che riducono la dimensione delle etichette delle cache secondarie. Se la cache secondaria è di un ordine di grandezza maggiore di quella primaria, e i dati della cache sono di un ordine di grandezza più grande delle etichette della cache, queste etichette di dati salvati può essere confrontato con l'area incrementale necessaria a immagazzinare i dati nella cache L1 ed L2.
 
Come menzionato prima, grandi computer hanno a volte un'altra cache tra quella L2 e la memoria principale chiamata cache L3. Questa cache è implementata generalmente su un chip separato dalla CPU, e come nel [[2004]], ha una capacità dai 2MB ai 256 MB. Queste cache costeranno ben oltre i $1000 da costruire, e i loro benefici dipenderanno dai percorsi di accesso delle applicazioni. Workstation x86 di fascia alta e server sono ora disponibili con un'opzione per la cache L3.
 
Infine, dall'altro lato della gerarchia della memoria, il [[Register file]] della CPU può essere considerato la più piccola, veloce cache nel sistema, con la speciale caratteristica che viene richiamata dal software—tipicamente da un [[compilatore]], siccome alloca registri che devono mantenere valori recuperati dalla memoria principale.
 
=== Esempio: architettura AMD K8 ===
Per illustrare sia la specializzazione che la gerarchia multilivello delle [[cache]], si può prendere come esempio l’architettura AMD [[Athlon 64]], nota anche come ''architettura K8''. La K8 utilizza quattro tipi di cache specializzate: una cache d’[[istruzione]], una [[Translation Lookaside Buffer|TLB]] d’istruzioni, una cache di dati e una TLB di dati. Ogni cache ha un ruolo specifico: La cache d’istruzioni mantiene copie di linee di memoria da 64 byte e recupera 16 byte per ciclo. Ogni byte in questa cache è memorizzato con 10 bit anziché 8, con i bit extra usati per indicare i limiti delle istruzioni (un esempio di precodifica). Questa cache utilizza un codice di parità per il controllo degli errori, che è meno pesante di un codice [[Error-correcting code|ECC]]; eventuali dati corrotti vengono ricaricati dalla memoria principale. La TLB d’istruzioni conserva le informazioni delle (PTE) e traduce gli indirizzi virtuali in indirizzi fisici durante il recupero delle istruzioni. È suddivisa in due sezioni: una per le mappature da 4 KB e una per mappature da 2 o 4 MB, facilitando un confronto completamente associativo in ciascuna sezione. La TLB dei dati ha due copie identiche che permettono di eseguire due traduzioni di indirizzi per ciclo, con la stessa suddivisione in base alla dimensione delle pagine. La cache dei dati memorizza linee da 64 byte ed è organizzata in 8 banchi da 8 KB ciascuno, capaci di fornire due dati da 8 byte per ciclo se presi da banchi diversi. Le etichette (tag) sono duplicate per gestire questi accessi simultanei. La K8 include anche cache di secondo livello (L2) per le TLB di istruzioni e dati, che immagazzinano solo mappature di pagine da 4 KB. Sia le cache L1 (dati e istruzioni) che le TLB sono rimpiazzate dalla cache L2, che è exclusive rispetto alle L1: una linea può risiedere solo in una tra cache L1 dati, cache L1 istruzioni o cache L2. La K8 gestisce anche informazioni di [[Predizione delle diramazioni|branch prediction]] associate alle istruzioni, immagazzinate sia nella cache L1 istruzioni sia nella cache L2 unificata. Per migliorare la predizione, utilizza bit extra in linee di cache protette da [[parità]] o [[Error-correcting code|ECC]], permettendo una migliore accuratezza grazie a una più ampia memoria storica dei percorsi di esecuzione.
Per illustrare sia la specializzazione che il multilivello delle cache, qui è la gerarchia della cache di un AMD [[Athlon 64]], la cui implementazione del core è conosciuta come ''architettura K8''.
La K8 ha 4 cache specializzate: una cache d'istruzioni, una d'istruzioni [[Translation Lookaside Buffer|TLB]], una cache di dati e una di dati TLB. Ognuna di queste cache è specializzata:
# La cache d'istruzione mantiene copie di line di memoria da 64 bytes, e recupera 16 bytes per ogni ciclo. Ogni byte in questa cache è immagazzinato in 10 bit piuttosto che 8, con gli extra bit che segnano i limiti delle istruzioni (Questo è un esempio del precoding). La cache ha solamente una protezione di [[Parity|parità]] piuttosto che una [[Error-correcting code|ECC]], perché la parità è più piccola, e ogni dato danneggiato può essere sostituito da un dato fresco dalla memoria (che ha sempre una copia aggiornata delle istruzioni).
# La TLB d'istruzioni tiene copia delle informazioni nella page table (PTE). Ogni ciclo di recupero d'istruzione ha il suo indirizzo virtuale tradotto attraverso questo TLB in uno fisico. Ogni informazione è sia da 4 che da 8 byte in memoria. Ogni TLB è suddivisa in due sezioni, una per mantenere il PTE che mappa 4KB, e una per tenere i PTE per mappare 4MB o 2MB. La suddivisione permette un circuito semplice per un confronto fully associative in ogni sezione. Il sistema operativo mappa sezioni differenti dello spazio d'indirizzi virtuali con differenti dimensioni di PTE.
# La TLB dei dati ha due differenti copie che mantengono le stesse informazioni. Le due copie permettono due accessi ai dati per ogni ciclo per tradurre indirizzi virtuali in fisici. Come la TLB d'istruzioni, questa TLB è suddivisa in due tipi d'informazioni.
# La cache dei dati mantiene copie di memoria di linee da 64 bytes. È suddivisa in 8 banchi (ognuno immagazzina 8KB di dati), e può recuperare due dati da 8-byte per ogni ciclo per tanto che questi dati siano in banchi diversi. Vi sono due copie delle etichette, perché ogni line da 64 byte è sparsa in tutti gli 8 banchi. Ogni copia di etichetta gestisce uno dei due accessi per ciclo.
 
La K8 ha anche cache a multilivello. Vi sono TLB d'istruzioni e dati di secondo livello, che immagazzinano solo mappature di PTE da 4KB. Sia le cache d'istruzioni che di dati e le varie TLB, possono essere riempite dalla grande cache unified di livello 2. Questa cache è esclusiva per entrambe le cache L1 di dati e istruzioni, il che significa che qualsiasi line a 8-byte può risiedere in una delle cache d'istruzioni L1, cache di dati L1 o cache L2. È comunque possibile per una linea nella cache dei dati di avere un PTE che sta anche in una delle cache TLB—il sistema operativo è responsabile di tenere le TLB coerenti scaricandone porzioni quando la page table nella memoria vengono aggiornate.
 
La K8 memorizza informazioni che non vengono mai immagazzinate in memoria—prediction information. Queste cache non vengono visualizzate nel diagramma precedente. Siccome è solito per questa classe di CPU, la K8 ha un [[Predizione delle diramazioni|branch prediction]] abbastanza complesso, con tabelle che aiutano a predire quali percorsi vengono presi e altre tabelle che predicono gli obbiettivi dei percorsi e salti. Alcune di queste informazioni vengono associate con delle istruzioni, sia nella cache d'istruzioni L1 sia in quella unified L2.
 
La K8 utilizza un interessante meccanismo per immagazzinare le informazioni di predizione con le istruzioni nella cache secondaria. Le linee nella cache secondaria sono protette dalla corruzione dei dati involontaria (ad esempio un colpo di [[particelle alfa]] tramite l'[[Error-correcting code|ECC]] o tramite la [[Parità (informatica)|parità]], in dipendenza che queste linee siano state rimosse dalla cache dei dati o da quella delle istruzioni. Siccome il controllo di parità occupa meno bit di quello ECC, le linee dalla cache d'istruzioni hanno pochi bit in avanzo. Questi bits vengono usati dal calcolo delle predizioni dei percorsi associati con quelli delle istruzioni. Il risultato finale è che il predittore di percorsi ha un grande tabella storica, quindi ha una migliore accuratezza.
 
=== Altre gerarchie ===
Altri processori hannoutilizzano altridiversi tipi di predittori. (adAd esempio, loil store-to-load bypass predictor nel DEC Alpha 21264), eè altriun svariaticaso predittorispecifico, specializzatie si sonoprevedono facilmente pensabiliulteriori dapredittori esserespecializzati da integratiintegrare neinelle futurifuture processoriarchitetture.
 
Questi predittori sonofunzionano cachein nelmodo sensosimile alle cache, chepoiché immagazzinano informazioni cheil sonocui costosecalcolo daè calcolare.costoso Alcunein delletermini terminologiedi utilizzatetempo nelo discutererisorse. iAlcuni predittoritermini sonousati lenella stessedescrizione didei quellepredittori, per le cache (si parla dicome hit nel predittore di percorsi), masono mutuati dalla terminologia delle cache. Tuttavia, i predittori non sonovengono generalmente pesaticonsiderati come parte della gerarchia delle cache.
 
LaL’architettura K8 mantiene le cache di istruzioni e i dati delle cache [[Cache coherency|coerenti]] nell'direttamente via hardware, ilgarantendo che significauna chescrittura unin immagazzinamentomemoria invenga un'istruzionesubito appenariflessa doponel l'immagazzinamentoflusso dell'istruzionedelle cambierà l'istruzioneistruzioni seguentesuccessive. Altri processori, come quelli nelladelle famigliafamiglie Alpha e MPSMIPS, sonoaffidano basatiinvece sulal software peril compito di mantenere lela coerenza tra cache d'istruzionid’istruzioni coerentie dati. GliIn immagazzinamentiquesti casi, una scrittura in memoria non sonoè garantitiautomaticamente divisibile essereal vistiflusso neldi fiume d'istruzioni a meno che unil programma chiaminon un'opzioneutilizzi delesplicitamente sistemaun’istruzione operativoo peruna assicurarsichiamata dellaal coerenza.sistema L'ideaoperativo èper quella di risparmiareassicurare la complessità dell'hardware sull'assunzione che il [[codice automodificante]] è rarocoerenza.
 
Questa scelta si basa sull’assunzione che il [[codice automodificante]] sia un fenomeno raro, permettendo di ridurre la complessità hardware necessaria per mantenere automaticamente la coerenza delle cache.
La gerarchia di cache si allarga se consideriamo il software come l'hardware. Il register file nel core di un processore può essere considerata una cache molto piccola, veloce i quali hit, fallimenti, e riempimenti sono previsti dal compilatore prima del tempo (vedi specialmente [[Loop nest optimization]]). I register file a volte hanno anch'essi una gerarchia: la [[Cray-1]] (circa del 1976) aveva 8 registri scalari e 8 registri d'indirizzi che erano generalmente utilizzabili, aveva anche 64 registri scalari e 64 registri d'indirizzi di tipo "B". I registri di tipo "B" potevano essere caricati più velocemente di quelli nella memoria principale, in quanto il Cray-1 non aveva una cache di dati.
La gerarchia delle cache si estende se consideriamo il software come parte dell’hardware. Il register file nel core di un processore può essere visto come una cache molto piccola e veloce, i cui hit, miss e riempimenti sono generalmente gestiti dal compilatore in anticipo (vedi in particolare Loop nest optimization). I register file possono avere a loro volta una gerarchia: ad esempio, la [[Cray-1]] (intorno al 1976) disponeva di 8 registri scalari e 8 registri d’indirizzi di uso generale, oltre a 64 registri scalari e 64 registri d’indirizzi di tipo "B". Questi registri di tipo "B" potevano essere caricati più rapidamente rispetto alla memoria principale, poiché la Cray-1 non disponeva di una cache dati tradizionale.
 
== Implementazione == <!-- paragrafo rivisitato il 23/03/2024. Mancano sempre le fonti e le note -->
Dato che le letture dalla cache sono operazioni comuni che richiedono più cicli di clock, il percorso critico nel design dei processori è spesso il trasferimento di dati dalla cache a un'istruzione. Questo percorso è essenziale per ottimizzare le prestazioni della CPU, poiché un accesso veloce alla cache L1 è fondamentale per ridurre i ritardi. La cache più semplice è una cache direttamente mappata, dove l'indirizzo virtuale è calcolato da un sommatore e utilizzato per indicizzare una SRAM (Static Random Access Memory), dalla quale vengono letti i dati corrispondenti. Non è necessario alcun controllo di etichetta nel ciclo interno, poiché le etichette non devono essere lette fino a quando non viene verificato un cache hit. Se la verifica fallisce, la cache viene aggiornata e il processo riparte.
 
Dato che le letture dalla cache sono operazioni frequenti che richiedono più cicli di clock, il percorso critico nel design dei processori è spesso il trasferimento dei dati dalla cache all’unità di esecuzione delle istruzioni. Questo passaggio è cruciale per ottimizzare le prestazioni della CPU, poiché un accesso rapido alla cache [[L1]] è fondamentale per ridurre i ritardi.
Le cache associative sono più complesse, poiché richiedono la lettura di alcuni dati per determinare quale punto della cache selezionare. Ad esempio, una cache level-1 set-associative N-way legge tutte le possibili etichette e dati in parallelo e seleziona quelli associati all'etichetta corrispondente. Le cache level-2 possono risparmiare potenza leggendo prima le etichette e selezionando quindi i dati dalla SRAM.
 
La cache più semplice è la cache direttamente mappata, dove una porzione dell’indirizzo virtuale viene calcolata tramite un sommatore e usata per indicizzare una memoria SRAM (Static Random Access Memory). I dati corrispondenti sono quindi letti direttamente da quella posizione. In questo schema, non è necessario un controllo delle etichette (tag) durante il ciclo interno di accesso, perché le etichette vengono verificate solo in un secondo momento per confermare un cache hit. Se la verifica fallisce (miss), la cache viene aggiornata e l’accesso ripetuto.
Il diagramma a destra illustra l'organizzazione degli indirizzi nella cache. Ad esempio, una cache 4KB, 2-way set-associative con linee da 64B utilizza 64 linee e legge due alla volta da una SRAM con 32 righe. Sebbene qualsiasi funzione degli indirizzi virtuali da 31 a 6 possa indicizzare etichette e dati SRAM, è più comune utilizzare i bit meno significativi.
 
Le cache associative sono più complesse, poiché richiedono la lettura simultanea di più etichette e dati per determinare quale linea selezionare. Ad esempio, una cache set-associative di primo livello (L1), N-way, legge in parallelo tutte le possibili etichette e dati di un set, scegliendo quelli che corrispondono all’indirizzo richiesto. Le cache di secondo livello (L2) possono ottimizzare il consumo energetico leggendo prima le etichette e poi selezionando i dati dalla SRAM solo se necessario.
Inoltre, una cache più moderna potrebbe essere di dimensioni diverse e utilizzare diverse tecniche, come indicizzazione virtuale, suggerimenti virtuali e indicizzazione fisica. Queste cache seguono un percorso di lettura simile, ma utilizzano diversi tipi di dati, come vhints anziché etichette, per determinare un cache hit.
 
Il diagramma a destra illustra l’organizzazione degli indirizzi in cache. Ad esempio, una cache di 4 KB, 2-way set-associative con linee da 64 byte è composta da 64 linee e può leggere due linee contemporaneamente da una SRAM organizzata in 32 righe. Sebbene qualsiasi funzione dei bit dell’indirizzo virtuale compresi tra 31 e 6 possa essere usata per indicizzare etichette e dati, in genere si utilizzano i bit meno significativi per migliorare l’efficienza.
Alcune implementazioni SPARC hanno migliorato le prestazioni delle loro cache L1 riducendo i ritardi del sommatore dell'indirizzo virtuale nei decoder SRAM.
 
Cache più moderne possono avere dimensioni variabili e adottare tecniche differenti, come l’indicizzazione virtuale, suggerimenti virtuali (vhints) e indicizzazione fisica. Pur seguendo un percorso di lettura simile, queste cache utilizzano diversi tipi di dati, come i vhints al posto delle etichette, per determinare un cache hit.
 
Alcune implementazioni dell’architettura [[SPARC]] hanno ottimizzato le prestazioni delle loro cache L1 riducendo i ritardi associati al calcolo dell’indirizzo virtuale nei decoder delle SRAM.
 
== Note ==
 
<references/>