Accesso puntatore e coerenza della cache

3

A mio avviso, quando si accede a una variabile, quella variabile e l'area circostante della memoria vengono inserite nella cache L1. Se sbaglio qui, per favore dimmelo.

Ora la mia domanda è, diciamo che ho una serie di puntatori, e voglio scorrere tutti loro eseguendo l'operazione X. Se devo prima accedere al puntatore, per ottenere l'indirizzo dei dati effettivi, vuol dire che la memoria vicino al puntatore verrà messa in cache, quindi la memoria vicino ai dati, quindi di nuovo al puntatore, quindi ai dati ecc.? Cache che picchia?

Se questo è il caso, come si può fare per mantenere la coerenza della cache?

    
posta Henry Elms 22.09.2013 - 04:54
fonte

1 risposta

6

Se i dati elimineranno o meno il puntatore dalla cache dipende da quanta memoria si tocca effettivamente, come gli indirizzi di memoria sono mappati alle linee della cache e dalla politica di sostituzione. La politica di sostituzione più comune è Least Recently Used (a volte solo approssimativa), dove i nuovi dati sostituiranno i dati utilizzati meno recentemente nella cache. Gli indirizzi di memoria vengono comunemente associati in insiemi N in cui più indirizzi di memoria si associano alla stessa riga della cache e ciascun indirizzo può eseguire il mapping su N linee cache ( vedi qui ). N sarà probabilmente 2 o 4.

In queste condizioni, quasi sicuramente non avrai il thrashing della cache se stai dereferenziando un puntatore, quindi utilizzando i dati SE i dati che tocchi dopo il dereferenziamento si inseriscono nella linea della cache. In entrambi gli accessi alla memoria, la linea cache usata meno di recente non è sicuramente la linea con il puntatore (nel caso dell'accesso ai dati), o i dati (nel caso del dereferenziamento del puntatore) poiché questi ultimi erano l'ultima (più recente) cache toccata Linee. Se tocchi più memoria di quella che si trova nella linea della cache con i dati dopo il dereferenziamento del puntatore, allora è possibile che sfrutti i dati del puntatore dalla cache, ma ciò dipende da dove i dati sono in memoria e quanto tocchi. È possibile che un maggiore accesso ai dati non sfrutti i puntatori se gli indirizzi non si collegano alla linea della cache con i puntatori o la linea del puntatore non è l'ultima utilizzata nel set di linee a cui gli indirizzi si riferiscono, ma poiché l'indirizzo di memoria della tua allocazione dei dati di solito non è noto fino a quando il programma viene eseguito, è difficile dire in ogni caso solo dal codice sorgente.

Probabilmente è possibile avere i dati e i puntatori allocati a specifici indirizzi di memoria solo per continuare a colpire la cache il più possibile, ma ciò richiederebbe conoscere le politiche della cache del processore che esegue il codice e che il compilatore non fa ottimizzazioni che cambiano i modelli di accesso ai dati senza che tu lo sappia. Sarebbe anche specifico per il processore, con diverse politiche di cache su diversi processori rendendo inutile qualsiasi micro ottimizzazione su questa scala. Come noto di seguito, è molto difficile trovare informazioni su politiche specifiche su processori comuni, quindi probabilmente non vale la pena di pensare a questo tipo di ottimizzazione. In generale, gli accessi agli indirizzi di memoria consecutivi sono il modo migliore per colpire la cache se si toccano le cose solo una volta (come nel ciclo sopra i puntatori). Non è necessaria altra ottimizzazione.

Questo è tutto presupponendo un singolo processore con un singolo thread. Più thread e più processori eseguono chiavi di ragionamento sulla cache. Più thread su un singolo processore significa che un altro thread potrebbe iniziare a girare nel mezzo del tuo loop e quando il thread inizia a funzionare di nuovo, la cache si trova in uno stato completamente diverso e l'intero processo ricomincia, ma altrimenti non molto è diverso.

Se si dispone di un ciclo a più thread (si pensi a un thread per puntatore) in esecuzione su più processori, l'intera faccenda della cache è piuttosto difficile da ragionare. L'ordine di esecuzione potrebbe essere riorganizzato o i puntatori consecutivi potrebbero essere dereferenziati su processori diversi. Entrambe le situazioni significano che la cache probabilmente non è stata innescata da precedenti dereferenze del puntatore o accessi ai dati. In questo caso, dovresti considerare i dati di chunking in modo che un thread acceda a un intervallo di puntatori anziché a uno solo, in modo che la cache possa effettivamente essere utilizzata.

Ricorda che la memoria che tocchi dopo la dereferenziazione deve essere PICCOLA. Alcuni (leggi 2 o 4) int s si inseriscono in una riga della cache, ma un oggetto con molti membri potrebbe non esserlo. Se accedi ai dati memorizzati più delle dimensioni di una linea cache dall'inizio dell'oggetto, stai sfrattando di più dalla cache e quindi hai più possibilità di sfrattare i puntatori.

Trovare dati sulle cache nei processori Intel e AMD è un po 'difficile, ma ho trovato questo collegamento dal 2010 dicendo che Intel utilizza una politica di pseudo LRU su una cache associativa a 4 vie per la cache L2 e la stessa politica per la cache L1 (non viene indicata alcuna associatività dell'insieme) su almeno una delle rispettive architetture del processore. Va notato che la politica di sostituzione è un componente integrale nel rendere la cache più veloce dei programmi. Non mi sorprenderebbe minimamente se fossero più complicati o più difficili da ragionare rispetto a quello che ho presentato sopra poiché l'LRU non modificata non è universalmente la migliore politica di sostituzione. Le policy di sostituzione effettive probabilmente hanno regole molto più complicate in nome del mantenimento di hit della cache alti, ma i dettagli esatti sarebbero scarsi dal momento che sono un vantaggio competitivo nelle guerre dei benchmark delle prestazioni.

    
risposta data 22.09.2013 - 09:10
fonte

Leggi altre domande sui tag