Innanzitutto un accesso alla memoria principale è molto costoso. Attualmente una CPU a 2 GHz (la più lenta una volta) ha 2G tick (cicli) al secondo. Una CPU (virtual core al giorno d'oggi) può recuperare un valore dai suoi registri una volta per tick. Poiché un nucleo virtuale è costituito da più unità di elaborazione (ALU - unità logica aritmetica, FPU ecc.), Può effettivamente elaborare alcune istruzioni in parallelo, se possibile.
Un accesso della memoria principale costa circa 70ns a 100ns (DDR4 è leggermente più veloce). Questa volta è fondamentalmente cercare la cache L1, L2 e L3 e poi colpire la memoria (inviare il comando al controller di memoria, che lo invia ai banchi di memoria), attendere la risposta e terminare.
100ns significa circa 200 tick. Quindi, in pratica, se un programma mancherebbe sempre delle cache che ogni accesso alla memoria, la CPU impiegherebbe circa il 99,5% del tempo (se legge solo la memoria) in attesa della memoria.
Per velocizzare le cose ci sono le cache L1, L2, L3. Usano la memoria direttamente sul chip e usano un diverso tipo di circuiti a transistor per memorizzare i bit dati. Ciò richiede più spazio, più energia ed è più costoso della memoria principale poiché una CPU viene solitamente prodotta utilizzando una tecnologia più avanzata e un guasto di produzione nella memoria L1, L2, L3 ha la possibilità di rendere la CPU priva di valore (difetto) le grandi cache L1, L2, L3 aumentano il tasso di errore che diminuisce la resa che diminuisce direttamente la ROI. Quindi c'è un enorme compromesso quando si tratta di dimensioni della cache disponibili.
(attualmente si creano più cache L1, L2, L3 per poter disattivare alcune parti per ridurre la possibilità che un difetto di produzione effettivo sia rappresentato dalle aree di memoria cache che restituiscono il difetto della CPU nel suo complesso).
Per dare un'idea temporizzazione (fonte: costs to access caches and memory)
- cache L1: 1 ns a 2ns (2-4 cicli)
- L2 cache: 3ns a 5ns (6-10 cicli)
- cache L3: 12ns a 20ns (24-40 cicli)
- RAM: 60ns (120 cicli)
Essendo mescolare diversi tipi di CPU queste sono solo stime, ma dare un go od idea cosa sta andando veramente quando viene recuperato un valore di memoria e potremmo avere un colpo o un errore in certi livelli di cache.
Quindi una cache accelera notevolmente l'accesso alla memoria notevolmente (60ns contro 1ns).
Recuperare un valore, archiviarlo nella cache per la possibilità di rileggerlo è positivo per le variabili a cui si accede spesso, ma per le operazioni di copia di memoria sarebbe ancora lento poiché si legge un valore, scrive il valore da qualche parte e non legge mai più il valore ... nessun hit della cache, dead slow (oltre a ciò può accadere in parallelo dato che abbiamo l'esecuzione fuori servizio).
Questa copia di memoria è così importante che ci sono diversi modi per accelerarlo.Nei primi tempi la memoria era spesso in grado di copiare la memoria al di fuori della CPU. È stato gestito direttamente dal controller di memoria, quindi un'operazione di copia di memoria non ha inquinato le cache.
Ma accanto a una semplice copia di memoria altri accessi seriali di memoria erano piuttosto comuni. Un esempio è l'analisi di una serie di informazioni. Avere un array di numeri interi e calcolare la somma, media, media o anche più semplice trovare un certo valore (filtro/ricerca) era un'altra classe molto importante di algoritmi eseguiti ogni volta su qualsiasi CPU generica.
Quindi analizzando il modello di accesso alla memoria era evidente che i dati vengono letti sequenzialmente molto spesso. C'era un'alta probabilità che se un programma legge il valore all'indice i, che il programma leggerà anche il valore i + 1. Questa probabilità è leggermente superiore alla probabilità che lo stesso programma legga anche il valore i + 2 e così via.
Quindi, dato un indirizzo di memoria, era (ed è ancora) una buona idea leggere avanti e recuperare valori aggiuntivi. Questo è il motivo per cui esiste una modalità boost.
L'accesso alla memoria in modalità boost significa che un indirizzo viene inviato e più valori vengono inviati in sequenza. Ogni valore addizionale inviato richiede solo circa 10ns (o anche più in basso).
Un altro problema era un indirizzo. L'invio di un indirizzo richiede tempo. Per indirizzare gran parte della memoria, è necessario inviare indirizzi di grandi dimensioni. Nei primi tempi ciò significava che il bus indirizzo non era abbastanza grande da inviare l'indirizzo in un singolo ciclo (tick) e più di un ciclo era necessario per inviare l'indirizzo aggiungendo più ritardo.
Una riga cache di 64 byte, ad esempio, significa che la memoria è divisa in blocchi di memoria distinti (non sovrapposti) di 64 byte di dimensione. 64 byte significa che l'indirizzo iniziale di ciascun blocco ha i sei bit di indirizzo più bassi da essere sempre zeri. Quindi, l'invio di questi sei zero bit ogni volta non è necessario aumentando lo spazio di indirizzamento 64 volte per qualsiasi numero di larghezza del bus di indirizzo (effetto di benvenuto).
Un altro problema risolve la riga della cache (oltre a leggere in anticipo e salvare/liberare sei bit sul bus degli indirizzi) è nel modo in cui la cache è organizzata. Ad esempio, se una cache viene suddivisa in blocchi (celle) a 8 byte (64 bit), è necessario memorizzare l'indirizzo della cella di memoria in cui questa cella cache contiene il valore. Se l'indirizzo fosse anche a 64 bit, significa che metà della dimensione della cache viene consumata dall'indirizzo risultante in un sovraccarico del 100%.
Poiché una linea cache è 64 byte e una CPU può utilizzare 64 bit - 6 bit = 58 bit (non è necessario memorizzare i bit zero troppo a destra) significa che è possibile memorizzare 64 byte o 512 bit con un sovraccarico di 58 bit (11% di spese generali). In realtà gli indirizzi memorizzati sono ancora più piccoli di questo, ma ci sono informazioni sullo stato (come la linea della cache valida e precisa, sporca e che deve essere riscritta nella ram ecc.).
Un altro aspetto è che abbiamo impostato la cache associativa. Non tutte le celle della cache sono in grado di memorizzare un determinato indirizzo ma solo un sottoinsieme di esse. Ciò rende ancora più piccoli i bit di indirizzo memorizzati necessari, consente l'accesso parallelo alla cache (è possibile accedere a ogni sottogruppo una sola volta ma indipendentemente dagli altri sottosistemi).
C'è ancora di più quando si tratta di sincronizzare l'accesso cache/memoria tra i diversi core virtuali, le loro unità di elaborazione multiple indipendenti per core e infine più processori su una scheda madre (che ci sono schede che ospitano fino a 48 processori e più).
Questa è fondamentalmente l'idea corrente del perché abbiamo linee cache. Il vantaggio di leggere in anticipo è molto alto e il caso peggiore di leggere un singolo byte fuori da una linea di cache e non leggere mai il resto è molto sottile poiché la probabilità è molto ridotta.
La dimensione della cache-line (64) è un saggia scambio scelto tra le più grandi linee di cache rende improbabile che l'ultimo byte di esso venga letto anche nel prossimo futuro, la durata che impiega a recuperare la linea di cache completa dalla memoria (e per riscriverla) e anche il sovraccarico nell'organizzazione della cache e la parallelizzazione dell'accesso alla cache e alla memoria.
Leggi questo: [cosa ogni programmatore dovrebbe sapere sulla memoria] (http://lwn.net/Articles/250967/). Quindi leggi di nuovo. Meglio (pdf) [fonte qui] (http://www.akkadia.org/drepper/cpumemory.pdf). – andersoj
Anche questo ha informazioni abbastanza buone relative alla tua query: http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Memory/introCache.html –