2010-12-27 15 views

risposta

1

Caveat: Non sono esperto in questo. Sto ancora cercando di imparare da solo. Ma poiché nessuno ha risposto negli ultimi due giorni, sembra che gli esperti sulle istruzioni della barriera di memoria non siano abbondanti. Quindi, ecco la mia comprensione ...

Intel è un sistema di memoria weakly-ordered. Ciò significa che il programma può eseguire

array[idx+1] = something 
idx++ 

ma la modifica idx può essere globalmente visibile (ad esempio per thread/processi in esecuzione su altri processori) prima della modifica matrice. Inserendo sfence tra le due istruzioni si assicurerà l'ordine di invio delle scritture all'FSB.

Nel frattempo, un altro processore gestisce

newestthing = array[idx] 

potrebbe essere memorizzata nella cache della memoria per gamma e ha una copia stantio, ma ottiene la versione aggiornata IDX a causa di una cache miss. La soluzione è utilizzare lfence in anticipo per garantire la sincronizzazione dei carichi.

This article o this article possono dare una migliore informazioni

+0

No, i negozi x86 sono ordinati per impostazione predefinita. Il riordino in fase di compilazione potrebbe produrre il riordino che descrivi (se non riesci a usare 'std :: atomic' con' memory_order_release' o più forte), ma i negozi dalle istruzioni x86 'mov [array + rcx], eax' /' mov [idx], rcx' diventerebbe globalmente visibile ad altri thread in quell'ordine. Solo gli archivi di streaming 'MOVNT' sono debolmente ordinati (quindi è necessario' sfence' dopo di essi prima di memorizzare in un flag 'buffer_ready'). Normalmente non hai mai bisogno di 'lfence', a meno che tu non stia usando carichi debolmente ordinati dalla memoria video o qualcosa del genere. –

+0

Vedere anche [la mia risposta su una domanda di sfence più recente] (https://stackoverflow.com/a/44866652/224132). Inoltre, gli articoli eccellenti di Jeff Preshing, come questo [debolezza forte modello di memoria] (http://preshing.com/20120930/weak-vs-strong-memory-models/) post. (È stato scritto 2 anni dopo aver postato questo messaggio. Non intendo essere scortese con una vecchia risposta, ma è quasi totalmente sbagliato, xD) –

+0

@PeterCordes Hai sbagliato completamente. 1) non hai bisogno di std :: atomic per ottenere l'ordine corretto. Infatti std :: atomic non esisteva fino a C++ 11. Quindi non esisteva al momento in cui è stato fatto il post. 2) lfence viene utilizzato per garantire un ordine di istruzioni corretto dal lato del consumatore. Quindi, se vuoi leggere il tuo buffer dopo aver visto il flag 'buffer_ready', allora usa lfence per assicurarti che la lettura dal buffer non avvenga prima. –

3

Ecco la mia comprensione, si spera accurato e abbastanza semplice da dare un senso:

(Itanium) Architettura IA64 permette di memoria di lettura e scrittura per essere eseguito in qualsiasi ordine, quindi l'ordine della memoria cambia dal punto di vista di un altro processore non è prevedibile a meno che non si utilizzino le recinzioni per far rispettare le scritture complete in un ordine ragionevole.

Da qui in poi, sto parlando di x86, x86 è fortemente ordinato.

Su x86, Intel non garantisce che un archivio fatto su un altro processore sarà sempre immediatamente visibile su questo processore. È possibile che questo processore abbia eseguito il carico (letto) in maniera speculativa abbastanza presto per non perdere l'archivio dell'altro processore (scrittura).

Le istruzioni di lettura/modifica/scrittura bloccate sono completamente coerenti sequenzialmente, quindi raramente è necessario utilizzare le fence su x86. Per questo motivo, in generale, gestisci già le operazioni di memoria dell'altro processore perché un xchg o cmpxchg bloccato lo sincronizzeranno.

Per quanto ne so, lfence drena la coda di caricamento della memoria e attende che la pipeline dell'unità di carico finisca qualsiasi operazione in corso. mfence va oltre e attende tutte le letture e le scritture di memoria, sfence fa lo stesso per solo i negozi (e svuota il combinatore di scrittura).

In sostanza, lfence scarta qualsiasi carico eseguito in modo speculativo.Carichi che potrebbero essere stati precedentemente eseguiti in modo speculativo verranno riemessi. in pratica lo sfence è il minimo necessario, di solito non è necessario se non si usa la memoria di combinazione di scrittura, cosa che si fa raramente se non si è uno sviluppatore in modalità kernel (driver).

Quindi, per riassumere, gli algoritmi che utilizzano istruzioni bloccate come xchg, o xadd o cmpxchg, ecc, funzioneranno senza recinti perché l'istruzione bloccata (nella maggior parte dei casi) fa tutto per sincronizzare. Qualsiasi codice ingannevole e privo di blocco che (per esempio) ha percorsi di codice in uscita che non usano quelle istruzioni di blocco potrebbe aver bisogno di lfence da qualche parte per evitare di perdere un negozio fatto da un altro processore. Il codice che è permaloso è raro e non è una buona pratica, ma potrebbe essere necessario in percorsi di codice estremamente caldi.

+0

Normalmente non hai bisogno di 'lfence' mai. Hai solo bisogno di 'sfence' [dopo lo streaming dei negozi' movnt' debolmente ordinato] (https://stackoverflow.com/a/44866652/224132). È necessario 'mfence' (o un'operazione' lock'ed) per ottenere coerenza sequenziale invece di solo rilascio/acquisizione. (Vedere [Riorganizzazione della memoria catturata nella legge] (http://preshing.com/20120515/memory-reordering-caught-in-the-act/) per un esempio.) –

+0

Di solito è necessario 'lfence' perché compiler C++. –

Problemi correlati