Informazioni sulla propagazione dell'aggiornamento della memoria nelle cache e nella RAM della CPU L1 / L2 / L3 x86 / x86-64

6

Sto cercando di capire in senso generale come vengono aggiornate L1 / L2 (e ora le cache L3) e come gli aggiornamenti vengono propagati in una CPU x86 / x86-64 multi-core agli altri core e infine alla RAM.

Supponendo una CPU a 4 core e 2 coppie di cache L1 / L2, in cui coppie di core condividono una coppia L1 / L2 comune e c'è un'interconnessione tra le 2 coppie L1 / L2. E le linee della cache sono larghe 64 bit. Quindi abbiamo:

  1. Core-0 / Core-1 su (L1 / L2) -0
  2. Core-2 / Core-3 attivo (L1 / L2) -1
  3. (L1 / L2) -0 è collegato a (L1 / L2) -1

Diciamo che c'è un thread T0 in esecuzione su Core-0 che sta scrivendo su una variabile intera a 64 bit chiamata X, e c'è un altro thread T1 su Core-3 che legge continuamente il valore della variabile X - Si prega di ignorare le condizioni logiche di gara per un momento.

Domanda: Supponendo che X sia stato memorizzato nella cache di L1 del Core-0. Quando T0 scrive un nuovo valore su X, la seguente sequenza di eventi è corretta?

  1. Il valore di X viene trasferito dal registro a L1-0
  2. Il valore di X viene spostato da L1-0 a L2-0
  3. Il valore di X viene spostato da L2-0 a L2-1
  4. il valore di X viene spostato da L2-1 a L1-1
  5. il valore di X viene spostato da L1-0 a RAM

Nota. I passaggi 2 e 4 potrebbero accadere contemporaneamente.

    
posta Giles Ramoni 01.01.2015 - 03:12
fonte

1 risposta

13

Innanzitutto se sei preoccupato per i recenti (ultimi 5-10 anni, da Nahalem?) Intel x86, allora il tuo architetto è un po 'fuori. Ogni core ha il proprio split di cache L1 da 128K (codice 64K / codice 64K). Oltre a ciò, ogni core ha una propria cache L2 che funge fondamentalmente da buffer tra la cache L1 e L3. Ogni socket ha la sua cache L3 (fino a 20 MB, penso). Le cache L1 e L2 sono piccole, semplici e veloci. La cache L3 è molto più grande e complessa (è suddivisa in piccoli segmenti che si trovano tutti su un anello con altre cose come il controller di memoria e il bridge QPI ad altri socket). (Si prega di ignorare le cose come il carico e memorizzare i buffer che renderà questo ancora più complesso.)

Inoltre, le linee della cache sono 64-byte.

Così sembra (ad esempio una macchina dual socket, con 4 core per CPU):

Core 0 (socket 0) -> L1-0 -> L2-0 -> L3-0 -> QPI link to socket 1
Core 1 (socket 0) -> L1-1 -> L2-1 -> ^
Core 2 (socket 0) -> L1-2 -> L2-2 -> |
Core 3 (socket 0) -> L1-3 -> L2-3 -> +

Core 4 (socket 1) -> L1-4 -> L2-4 -> L3-1 -> QPI link to socket 0
Core 5 (socket 1) -> L1-5 -> L2-5 -> ^
Core 6 (socket 1) -> L1-6 -> L2-6 -> |
Core 7 (socket 1) -> L1-7 -> L2-7 -> +

Pre Nahalem, le cache L2 sono state condivise tra coppie di core. Non ho dovuto fare un sacco di lavoro sulle prestazioni su questo in un attimo, quindi non sono davvero sicuro delle sottigliezze lì.

La cache L3 è completamente inclusiva della cache L1 e L2 sotto di essa. La cache contiene i valori "corretti" per tutti gli indirizzi di memoria. Più corretto della memoria principale, poiché le scritture possono rimanere in L3 per un po 'di tempo prima di andare in memoria (memorizzazione nella cache di write-back). Tutte le cache sono coerenti. Cioè, non avrai mai avuto due valori diversi per la stessa posizione di memoria. Questa coerenza è garantita da una versione del protocollo MESI, chiamata MESIF (per Intel, AMD ha una diversa strategia di caching e usa MEOSI e ha organizzato il loro caching in modo diverso).

Dato che L1 e L2 sono privati del core, la coerenza deve essere gestita solo al livello L3 (penso, non sono stato in grado di ottenere una risposta definitiva su questo). Le interconnessioni della cache hanno quattro corsie: dati, richiesta, conferma e ficcanaso (per tenerti aggiornato su altre operazioni di memoria).

Ora possiamo rispondere alle tue domande.

Se un thread su Core 0 sta leggendo un indirizzo, l'indirizzo risiederà in L1-0 e L3-0 nello stato Exclusive, Shared o Forward (tutti e tre mostrano che l'indirizzo non è modificato e memorizzato nella cache). Ora, Core-4 vuole scrivere su di esso. Una Request / Read-For-Ownership invierà la linea cache dall'altra cache L3 (L3-0) e farà in modo che le altre cache contrassegnino le loro copie come non valide. Ora sarà in L1-4 e L3-1 (contrassegnato come esclusivo).

(Qui è dove l'ignorare i buffer del negozio è stato semplificato molto.)

Core-4 scriverà da un registro nella cache L1-4. Causando il passaggio della linea allo stato Modificato. Questo viene propagato alla cache L3-1 (poiché è completamente inclusivo).

Ora, Core-0 vuole leggere di nuovo. La cache L1-0 non è valida a quell'indirizzo, quindi invia una richiesta di lettura che perde la cache L3-0 e fa in modo che l'L3-1 invii la linea cache indietro. L'L3-1 ora afferma che l'indirizzo è condiviso, e L3-0 mantiene la linea come Forward (il richiedente più recente ottiene la responsabilità di inoltro).

Chiaro come fango? Potrebbero esserci alcune modifiche a questo per ripulire parte del linguaggio su cui potrei essere troppo vago.

    
risposta data 01.01.2015 - 19:12
fonte

Leggi altre domande sui tag