lunedì 12 settembre 2011

Barriere di memoria / Recinzioni



In questo articolo parlerò la tecnica più fondamentale nella programmazione concorrente conosciuto come barriere di memoria, o recinzioni, che rendono lo stato di memoria all'interno di un processore ad altri processori visibile. CPU hanno impiegato molte tecniche per cercare di accogliere il fatto che unità di esecuzione della CPU prestazioni ha notevolmente superato le prestazioni della memoria principale. Nel mio " Scrivi Combinando articolo "Ho toccato solo una di queste tecniche.La tecnica più comune impiegato da CPU per nascondere la latenza di memoria è di istruzioni pipeline e poi passare notevole impegno e risorse, per cercare di riordinare questi oleodotti di ridurre al minimo bancarelle relativi alla cache miss. Quando un programma viene eseguito non importa se le sue istruzioni sono riordinati fornito il risultato finale è lo stesso raggiunto. Per esempio, all'interno di un ciclo non importa se il contatore del ciclo viene aggiornato se nessuna operazione all'interno del ciclo che utilizza. Il compilatore e CPU sono liberi di riordinare le istruzioni per utilizzare al meglio la CPU a condizione che sia aggiornato per il momento l'iterazione successiva è in procinto di iniziare. Anche oltre l'esecuzione di un ciclo di questa variabile può essere memorizzato in un registro e mai spinto fuori di cache o la memoria principale, quindi non è mai visibile a un'altra CPU. core contengono unità di esecuzione multiple. Ad esempio, una moderna CPU Intel contiene 6 unità di esecuzione che può fare una combinazione di aritmetica, logica condizionale, e la manipolazione della memoria. Ogni unità di esecuzione può fare una qualche combinazione di queste attività.Queste unità di esecuzione in parallelo permettendo istruzioni da eseguire in parallelo. Questo introduce un altro livello di non-determinismo di ordinare programma che è stato osservato da un altro CPU. Infine, quando una cache-miss si verifica, una CPU moderna può fare una supposizione sui risultati di un carico di memoria e continuare l'esecuzione sulla base di questo assunto fino il carico restituisce i dati effettivi. forniti "per il programma" è conservata la CPU, ed il compilatore, sono liberi di fare quello che ritengono opportuno migliorare le prestazioni.

Figura 1.

Carica e negozi per le cache e la memoria principale sono tamponata e ri-ordinato utilizzando il, negozio di carico, e scrivono-che combina buffer. I buffer sono code associativi che consentono una rapida ricerca. Questa ricerca è necessaria quando un carico in seguito ha bisogno di leggere il valore di un negozio di precedenti che non hanno ancora raggiunto la cache. La figura 1 mostra sopra una visione semplificata di una moderna CPU multi-core. Essa mostra come le unità di esecuzione può utilizzare i registri locali e buffer per gestire la memoria mentre viene trasferito avanti e indietro dalla cache sub-sistema. In un multi-threaded tecniche di ambiente devono essere impiegate per rendere visibili i risultati del programma in modo tempestivo modo. Io non coprirà la coerenza della cache in questo articolo. Basta pensare che la memoria una volta che è stato spinto nella cache poi un protocollo di messaggi si verifica per assicurare tutte le cache siano coerenti per i dati condivisi. Le tecniche per rendere visibile la memoria da un core del processore sono conosciute come barriere di memoria o recinzioni. barriere memoria fornire due proprietà. In primo luogo, mantenere l'ordine programma visibile esternamente, garantendo tutte le istruzioni per entrambi i lati della barriera appaiono nell'ordine corretto programma se osservati da un'altra CPU e, dall'altro, rendono visibile la memoria, garantendo i dati sono propagate alla cache di sub-sistema. barriere di memoria sono un argomento complesso. Essi sono implementati in modo molto diverso tra architetture di CPU. Ad un estremo dello spettro c'è un modello di memoria relativamente forte sul CPU Intel che è più semplice dire che il modello di memoria debole e complessa su un DEC Alpha con la sua cache partizionato in aggiunta a strati cache. Dal momento che le CPU x86 sono le più comuni per la programmazione multi-threaded Cercherò di semplificare a questo livello. Conservare Barriera Una barriera negozio, "sfence istruzioni "su sistemi x86, le forze di tutte le istruzioni prima di archiviare la barriera che accada prima della barriera e hanno il negozio buffer lavata di cache per le CPU su cui è rilasciato. In questo modo lo stato di programma visibile a altre CPU in modo che possano agire su di essa, se necessario. Un buon esempio di questo in azione è il seguente codice semplificato dal BatchEventProcessor nel Disruptor. Quando la sequenza viene aggiornato altri consumatori e produttori di sapere fino a questo consumo è progredita, e quindi può prendere i provvedimenti opportuni.Tutti gli aggiornamenti precedenti alla memoria quello che è successo prima della barriera sono ora visibili.



01.privato volatili lunga sequenza = RingBuffer.INITIAL_CURSOR_VALUE;
02./ / da dentro il metodo run ()
03.T = evento nullo ;
04.lunga sequenza nextSequence = + 1L;
05.mentre (in esecuzione)
06.{
07.provare
08.{
09.finale lungo availableSequence =
10.dependencyBarrier.waitFor (nextSequence);
11. 
12.mentre (nextSequence <availableSequence =)
13.{
14.evento = dependencyBarrier.getEvent (nextSequence);
15.eventHandler.onEvent (evento, availableSequence nextSequence ==);
16.nextSequence + +;
17.}
18. 
19.sequenza = event.getSequence ();
20./ / store barriera inserito qui!
21.}
22.catturare finale Exception ex)
23.{
24.exceptionHandler.handle (ex, evento);
25.sequenza = event.getSequence ();
26./ / store barriera inserito qui!
27.nextSequence = event.getSequence () + 1L;
28.}
29.}
Barriera di carico
Una barriera di carico, " lfence istruzioni "su sistemi x86, le forze di tutte le istruzioni di carico dopo la barriera per accadere dopo la barriera e poi aspettare il caricamento del buffer di scarico per quella CPU. Questo rende stato del programma esposto da altre CPU visibili a questa CPU prima di fare ulteriori progressi. Un buon esempio di questo è quando la sequenza BatchEventProcessor di cui sopra viene letto da produttori o consumatori, le barriere corrispondente del Disruptor. Barriera completa una barriera completa, " mfence istruzioni "su x86, è un composto di caricare e memorizzare le barriere accadendo su una CPU. modello di memoria Java Nel modello di memoria di Java uno volatilicampo ha una barriera negozio inserito dopo una scrittura e una barriera di carico inseriti prima di una lettura di esso.Qualificato finale campi di una classe hanno una barriera negozio inserito dopo la loro inizializzazione per assicurare questi campi sono visibili una volta che il costruttore completa quando un riferimento all'oggetto è disponibile. istruzioni atomica e Software Serrature istruzioni atomica, come il " blocco ... " istruzioni x86, sono effettivamente una barriera completa come bloccare la memoria sotto-sistema per eseguire un'operazione e hanno garantito totale dell'ordine, anche attraverso le CPU. Il software si blocca di solito impiegano le barriere della memoria, o istruzioni atomiche, per ottenere visibilità e mantenere l'ordine del programma. impatto sulle prestazioni delle barriere di memoria Memory barriere impediscono una CPU di eseguire un sacco di tecniche per nascondere la latenza della memoria quindi hanno un costo significativo delle prestazioni che devono essere considerati . Per ottenere le massime prestazioni è meglio per modellare il problema in modo che il processore può fare le unità di lavoro, poi tutte le barriere di memoria necessaria verifica sui confini di queste unità di lavoro. Questo approccio consente al processore di ottimizzare le unità di lavoro senza alcuna restrizione. C'è un vantaggio per il raggruppamento delle barriere memoria necessaria in quel buffer lavata dopo il primo sarà meno costosa, perché nessun lavoro sarà in corso per ricaricarle. 

Nessun commento:

Posta un commento

Nota. Solo i membri di questo blog possono postare un commento.