Esercizio 22cache a due vie LRU con write-back
In questa pagina 7
Testo (compitino di Architettura degli Elaboratori, UniPD, del 17 novembre 2015, esercizio 8). Sia data la seguente sequenza di indirizzi in lettura (l) o scrittura (s) emessi dalla CPU e la memoria abbia il contenuto esadecimale indicato.
| # | indirizzo (binario) | l/s | byte scritto (hex) |
|---|---|---|---|
| 1 | 0001 0000 1001 |
l | |
| 2 | 0001 0000 1101 |
s | AB |
| 3 | 0001 0000 1110 |
s | 39 |
| 4 | 0001 0001 1100 |
l | |
| 5 | 0001 0000 1000 |
s | D4 |
| 6 | 0001 0001 1110 |
l | |
| 7 | 0001 0000 1010 |
s | 98 |
| 8 | 0001 0010 0001 |
l |
| indirizzo | byte | byte | byte | byte |
|---|---|---|---|---|
| 100 | 08 | 00 | 07 | 02 |
| 104 | 00 | 00 | 00 | 00 |
| 108 | AE | 59 | AD | 23 |
| 10C | A1 | 42 | 90 | 75 |
| 110 | B9 | 16 | 00 | 00 |
| 114 | 0A | 07 | 03 | 71 |
| 118 | 3E | 13 | 71 | 23 |
| 11C | A1 | 82 | 90 | 15 |
| 120 | FF | C6 | AD | 00 |
| 124 | E9 | 16 | 05 | 00 |
Si assuma che la dimensione di parola coincida con un byte e la presenza di una cache di ampiezza 32 B, dimensione di blocco 2 B, inizialmente vuota, ad associazione a 2 vie, rimpiazzo LRU, politica di scrittura write-back e gestione dei miss in scrittura con write allocate. Si indichino i campi dell'indirizzo, la suddivisione della cache e come cambiano il contenuto della cache e quello della memoria.
Teoria: Memoria cacheBlocchi, linee ed etichette; scomposizione dell'indirizzo; associazione diretta, completamente associativa e associativa a insiemi con calcolo dei campi; politiche di rimpiazzo (LRU, FIFO, casuale); politiche di scrittura (write-through, write-back con bit sporco, write-allocate); dimensione del blocco; cache multilivello e separate; come ridurre i miss; quesiti sui campi dell'indirizzo.Memoria cache →. Esercizi analoghi: Esercizio 9 · cache con blocchi di due parole, Esercizio 11 · cache a due vie con rimpiazzo FIFO.
Campi e struttura
- Blocco di 2 B → parola 1 bit.
- Linee: ; a 2 vie → insiemi → set 3 bit.
- Etichetta: bit.
(La soluzione allegata al testo scrive "4 set, ognuno di 2 linee" ma indica correttamente i campi etichetta 8 bit, set 3 bit, parola 1 bit: con 3 bit di set gli insiemi sono 8, ciascuno di 2 linee da 2 B; i risultati dell'esempio sono quelli che si ottengono con 8 insiemi.)
L'indirizzo 0001 0000 1001 ha etichetta 00010000 (10 esadecimale), set 100 (4), parola 1.
Regole applicate
- LRU: nell'insieme pieno esce la linea usata meno di recente (un accesso, in lettura o scrittura, "usa" la linea).
- Write-back: la scrittura modifica solo la cache e mette a 1 il bit sporco (nella tabella un
*dopo il contenuto). La memoria si aggiorna solo quando una linea sporca viene rimpiazzata, e allora si riscrive l'intero blocco. - Write allocate: su miss in scrittura il blocco viene prima caricato, poi modificato in cache (e resta sporco).
Evoluzione
| # | indirizzo | accesso | etichetta | set | parola | esito | linea | contenuto della linea dopo l'accesso | memoria / note |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 109 |
l | 10 | 100 | 1 | miss | 0 | AE 59 | |
| 2 | 10D |
s AB | 10 | 110 | 1 | miss | 0 | A1 AB* | write allocate: carica A1 42, scrive AB, sporco |
| 3 | 10E |
s 39 | 10 | 111 | 0 | miss | 0 | 39 75* | write allocate: carica 90 75, sporco |
| 4 | 11C |
l | 11 | 110 | 0 | miss | 1 | A1 82 | l'insieme 110 ha ora due linee |
| 5 | 108 |
s D4 | 10 | 100 | 0 | hit | 0 | D4 59* | stesso blocco del passo 1; sporco |
| 6 | 11E |
l | 11 | 111 | 0 | miss | 1 | 90 15 | |
| 7 | 10A |
s 98 | 10 | 101 | 0 | miss | 0 | 98 23* | write allocate: carica AD 23 |
| 8 | 121 |
l | 12 | 000 | 1 | miss | 0 | FF C6 |
Esito: 1 hit (accesso 5) e 7 miss. Nessun rimpiazzo: in nessun passo un insieme già pieno riceve un nuovo blocco (al più due blocchi per insieme), quindi la memoria non cambia per tutto l'esercizio: le quattro scritture (AB, 39, D4, 98) vivono solo nella cache, nelle linee marcate *. In un sistema reale quei dati arriverebbero in memoria solo al rimpiazzo della linea o con una operazione di svuotamento (flush).
Stato finale
| insieme | linea 0 | linea 1 |
|---|---|---|
| 000 | etichetta 12: FF C6 |
vuota |
| 100 | etichetta 10: D4 59 (sporca) |
vuota |
| 101 | etichetta 10: 98 23 (sporca) |
vuota |
| 110 | etichetta 10: A1 AB (sporca) |
etichetta 11: A1 82 |
| 111 | etichetta 10: 39 75 (sporca) |
etichetta 11: 90 15 |
Che cosa cambierebbe con write-through
Con write-through ogni s aggiornerebbe subito la memoria: M[10D] = AB, M[10E] = 39, M[108] = D4, M[10A] = 98 (quattro scritture di un byte, in memoria da subito); con write-back nessuna scrittura in memoria in questo esempio, ma un eventuale rimpiazzo successivo di una linea sporca costerebbe la riscrittura di 2 byte. Vedi Esercizio 21 · cache a due vie FIFO con write-through per il caso opposto.
Verifica con un simulatore
def cache_wb(accessi, memoria, byte_cache, blocco, vie):
"""LRU, write-back, write allocate. Restituisce esiti, cache e memoria."""
insiemi = byte_cache // blocco // vie
righe = {i: [] for i in range(insiemi)} # per insieme: linee dal meno al più recentemente usato
esiti = []
for a, op, val in accessi:
s, base = (a // blocco) % insiemi, a - a % blocco
et = a // blocco // insiemi
linea = next((l for l in righe[s] if l["et"] == et), None)
hit = linea is not None
if hit:
righe[s].remove(linea)
else:
if len(righe[s]) == vie:
v = righe[s].pop(0) # LRU: la prima della lista
if v["sporca"]:
vb = (v["et"] * insiemi + s) * blocco
for k in range(blocco):
memoria[vb + k] = v["dati"][k] # riscrive l'intero blocco
linea = {"et": et, "dati": [memoria.get(base + k, 0) for k in range(blocco)], "sporca": False}
righe[s].append(linea) # ora è la più recente
if op == "s":
linea["dati"][a % blocco] = val
linea["sporca"] = True
esiti.append("hit" if hit else "miss")
return esiti, righe, memoria
memoria = {}
for ind, byte4 in [(0x100, "08 00 07 02"), (0x104, "00 00 00 00"), (0x108, "AE 59 AD 23"), (0x10C, "A1 42 90 75"),
(0x110, "B9 16 00 00"), (0x114, "0A 07 03 71"), (0x118, "3E 13 71 23"), (0x11C, "A1 82 90 15"),
(0x120, "FF C6 AD 00"), (0x124, "E9 16 05 00")]:
for k, b in enumerate(byte4.split()):
memoria[ind + k] = int(b, 16)
originale = dict(memoria)
accessi = [(0x109, "l", None), (0x10D, "s", 0xAB), (0x10E, "s", 0x39), (0x11C, "l", None),
(0x108, "s", 0xD4), (0x11E, "l", None), (0x10A, "s", 0x98), (0x121, "l", None)]
esiti, righe, memoria = cache_wb(accessi, memoria, 32, 2, 2)
print(esiti) # ['miss', 'miss', 'miss', 'miss', 'hit', 'miss', 'miss', 'miss']
print(memoria == originale) # True: nessuna scrittura in memoria
for s in range(8):
print(format(s, "03b"), [(hex(l["et"]), [format(b, "02X") for b in l["dati"]], l["sporca"]) for l in righe[s]])Errori comuni
- Applicare write-through: scrivere subito in memoria. Con write-back la memoria resta uguale finché non si rimpiazza una linea sporca.
- Dimenticare il write allocate: un
sche manca va trattato come una lettura che carica il blocco e poi modifica un byte. - Con LRU, non aggiornare l'"uso" sugli hit (nell'accesso 5 la linea 0 dell'insieme
100diventa la più recente). - Contare 4 insiemi invece di 8: i 3 bit di set sono dati dal rapporto .
- Riscrivere solo il byte modificato al rimpiazzo di una linea sporca: va riscritto tutto il blocco.
Versione ripasso
Compitino del 17 novembre 2015: cache da 32 B, blocchi da 2 B, 2 vie, LRU, write-back, write allocate, indirizzi di 12 bit. Teoria: Memoria cacheBlocchi, linee ed etichette; scomposizione dell'indirizzo; associazione diretta, completamente associativa e associativa a insiemi con calcolo dei campi; politiche di rimpiazzo (LRU, FIFO, casuale); politiche di scrittura (write-through, write-back con bit sporco, write-allocate); dimensione del blocco; cache multilivello e separate; come ridurre i miss; quesiti sui campi dell'indirizzo.Memoria cache →.
Campi: parola 1 bit; linee ; insiemi → set 3 bit; etichetta bit (la soluzione allegata dice "4 set" ma i campi sono quelli con 8 insiemi).
Regole: LRU = esce la linea usata meno di recente (anche gli hit contano come uso); write-back = la scrittura modifica solo la cache e mette a 1 il bit sporco; al rimpiazzo di una linea sporca si riscrive l'intero blocco; write allocate = su miss in scrittura si carica il blocco e poi si modifica.
Esito (109 l, 10D s AB, 10E s 39, 11C l, 108 s D4, 11E l, 10A s 98, 121 l): miss, miss, miss, miss, hit, miss, miss, miss → 1 hit e 7 miss. Nessuna linea viene rimpiazzata, quindi la memoria non cambia: le scritture AB, 39, D4, 98 stanno solo nelle linee sporche (A1 AB*, 39 75*, D4 59*, 98 23*).
Errori comuni: write-through al posto di write-back; write allocate dimenticato; LRU non aggiornato sugli hit; 4 insiemi invece di 8; riscrittura del solo byte modificato.