Che è una migliore barriera di scrittura su x86: blocco + addl o xchgl?
-
26-09-2019 - |
Domanda
usi Linux Kernel lock; addl $0,0(%%esp)
come barriera di scrittura, mentre gli utilizzi di libreria RE2 xchgl (%0),%0
come barriera di scrittura. Qual è la differenza e che è meglio?
non x86 richiedono anche istruzioni barriera di lettura? RE2 definisce la sua funzione di barriera di lettura come non presente in 86 mentre Linux definisce come sia lfence
o no-op seconda che SSE2 è disponibile. Quando è necessario lfence
?
Soluzione
Il " serratura; addl $ 0,0 (%% esp) " è più veloce nel caso in cui abbiamo testare il 0 stato di variabili di blocco a (esp %%) indirizzo. Poiché aggiungiamo 0 al valore variabile serratura e il flag di zero è impostato a 1 se il valore della variabile di blocco all'indirizzo (%% esp) è 0.
lfence di Intel foglio di dati:
Esegue un'operazione serializzazione su tutte le istruzioni del carico di memoria che sono stati rilasciati prima della LFENCE istruzioni. Questo serializzazione garantisce un funzionamento che ogni carico istruzione che precede in programma ordinare l'istruzione è LFENCE visibili globalmente prima di qualsiasi carico istruzione che segue il LFENCE istruzione è globalmente visibile.
( Nota del redattore: mfence
o un'operazione lock
ed è il recinto solo utile (dopo un negozio) per sequenziale coerenza . lfence
non blocco StoreLoad riordino dal buffer negozio.)
Per esempio: istruzione di scrittura memoria come 'mov' sono atomiche (che non hanno bisogno del prefisso lock) se ci siano correttamente allineati. Ma questa operazione viene normalmente eseguita nella cache CPU e non saranno visibili globalmente in questo momento per tutti gli altri thread, perché la memoria recinzione deve essere eseguita prima di fare questa discussione attendere esercizi precedenti sono visibili ad altri thread.
Quindi, la differenza principale tra queste due istruzioni è che xchgl istruzione non avrà alcun effetto sulle bandiere condizionali. Certamente possiamo testare la variabile di stato serratura con Blocco cmpxchg di istruzioni ma questo è ancora più complesso di quello di Blocco aggiungere $ 0 di istruzioni.
Altri suggerimenti
Citando le IA32 manuali (Vol 3A, capitolo 8.2: Memoria di ordinazione):
In un sistema a singolo processore per regioni di memoria definita come cacheable write-back, il modello di memoria di ordinamento rispetta i seguenti principi [..]
- Letture non vengono riordinati con altri legge
- scritture non sono riordinati con anziani legge
- scrive di memoria non vengono riordinati con le altre scritture, con l'eccezione di
- scrive eseguito con l'istruzione
CLFLUSH
- streaming di negozi (scrive) eseguito con le istruzioni di movimento non-temporali ([elenco di istruzioni qui])
- operazioni sulle stringhe (vedi paragrafo 8.2.4.1)
- Letture può essere riordinata con scritture più anziani in luoghi diversi, ma non con le scritture più anziani nella stessa posizione.
- legge o scrive non può essere riordinata con le istruzioni di I / O, istruzioni bloccate, o istruzioni serializzazione
- Legge non può passare istruzioni
LFENCE
eMFENCE
- Scrive non può passare istruzioni
SFENCE
eMFENCE
Nota: L ' "In un sistema a singolo processore" sopra è leggermente fuorviante. Le stesse regole valgono per ogni (logico) processore individualmente; il manuale passa poi a descrivere le regole di ordinamento addizionali tra più processori. L'unica parte su di esso di pertinenza la questione è che
- istruzioni Locked hanno un ordine totale.
In breve, fino a quando si sta scrivendo nella memoria write-back (che è tutta la memoria potrai mai vedere finché non sei un programmatore di driver o grafica), la maggior parte delle istruzioni x86 sono quasi in sequenza coerente - l'unico riordino una CPU x86 in grado di eseguire è di riordino in seguito (indipendente) legge da eseguire prima di operazioni di scrittura. La cosa principale di barriere scrittura è che hanno un prefisso lock
(implicita o esplicita), che vieta qualsiasi riordino e assicura che le operazioni è visto nello stesso ordine da tutti i processori in un sistema multi-processore.
Inoltre, nella memoria write-back, legge non sono mai riordinate, quindi non c'è bisogno di barriere leggere. processori x86 recenti hanno un modello di coerenza di memoria più debole per lo streaming di negozi e la memoria write-combinata (comunemente usato per la memoria grafica mappato). Ecco dove le varie istruzioni fence
entrano in gioco; non sono necessari per qualsiasi altro tipo di memoria, ma alcuni driver del kernel di Linux fanno patto con la memoria write-combinata in modo che appena definiti la loro lettura barriera che modo. L'elenco dei modelli ordinazione per ogni tipo di memoria è nella Sezione 11.3.1 nel Vol. 3A del IA-32 manuali. Versione corta: write-through, write-back e Write-Protected consentire speculativa legge (seguendo le regole come sopra), la memoria Uncacheable uncachable e Strong ha forti garanzie di ordinazione (nessun riordino processore, letture / scritture vengono immediatamente eseguite, utilizzati per MMIO ) e scrittura della memoria combinato ha ordinamento debole (per esempio le regole di ordinamento rilassato che le recinzioni bisogno).
lock addl $0, (%esp)
è un sostituto per mfence
, non lfence
.
L'uso a caso è quando avete bisogno di bloccare StoreLoad riordino (l'unica che forte modello di memoria di 86 permette), ma non è necessario un intervento RMW atomica su una variabile condivisa. https://preshing.com/20120515/memory-reordering-caught- in-the-act /
es. supponendo std::atomic<int> a,b
allineato:
movl $1, a a = 1; Atomic for aligned a
# barrier needed here
movl b, %eax tmp = b; Atomic for aligned b
Le opzioni disponibili sono:
- Fare un negozio sequenziale-coerenza con
xchg
, per esempiomov $1, %eax
/xchg %eax, a
in modo da non avete bisogno di una barriera separata; è parte del negozio. Penso che questa sia l'opzione più efficiente sulla maggior parte dell'hardware moderno; C ++ 11 compilatori diversi dall'uso gccxchg
per i negozi seq_cst. - Usa
mfence
come una barriera. (Usi gccmov
+mfence
per i negozi seq_cst). -
Usa
lock addl $0, (%esp)
come una barriera. Qualsiasi istruzionelock
ed è una barriera piena. Blocco fa xchg hanno lo stesso comportamento come mfence?(o ad un altro luogo, ma la pila è quasi sempre privata e caldo in L1d, quindi è un po 'buon candidato. Tuttavia, ciò può creare una catena di dipendenza di qualcosa che usando i dati in fondo alla pila.)
Si può utilizzare solo xchg
come una barriera piegandolo in un negozio perché scrive incondizionatamente la posizione di memoria con un valore che non dipende il vecchio valore.
Quando è possibile, utilizzando xchg
per un negozio seq-CST è probabilmente la cosa migliore, anche se si legge anche dalla posizione condivisa. mfence
è più lento del previsto sulla recente CPU Intel ( sono un sacco e memorizza le uniche istruzioni che viene riordinati? ), anche il blocco out-of-ordine di esecuzione delle istruzioni non di memoria indipendenti allo stesso modo lfence
fa.
Potrebbe anche essere la pena utilizzare lock addl $0, (%esp)/(%rsp)
invece di mfence
anche quando mfence
è disponibile, ma non ho sperimentato con i lati negativi. Utilizzando -64(%rsp)
o qualcosa potrebbe rendere meno probabilità di allungare una dipendenza di dati su qualcosa di caldo (un locale o un indirizzo di ritorno), ma che può rendere strumenti come valgrind infelice.
lfence
non è mai utile per l'ordinazione di memoria a meno che non si sta leggendo da RAM video (o qualche altro WC debolmente ordinato regione) con carichi MOVNTDQA.
serializzazione esecuzione fuori ordine (ma non il buffer negozio) non è utile per fermare StoreLoad riordino (l'unica che forte modello di memoria di 86 permette il normale WB (write-back) regioni di memoria).
I casi d'uso reali per lfence
sono per bloccare esecuzione fuori ordine di rdtsc
per tempi molto brevi blocchi di codice, o per la mitigazione Spettro bloccando speculazione attraverso un salto condizionato o indiretta.
Quando devo utilizzare _mm_sfence _mm_lfence e _mm_mfence (la mia risposta e @ risposta di BeeOnRope) per di più sul perché lfence
non è utile, e quando utilizzare ciascuna delle istruzioni barriera. (O in miniera, le intrinseche C ++ durante la programmazione in C ++, invece di asm).
Per inciso alle altre risposte, gli sviluppatori HotSpot trovarono che lock; addl $0,0(%%esp)
con uno spostamento potrebbe non essere ottimale, su alcuni processori esso può introdurre falsi dipendenze dei dati ; relativi JDK bug .
Toccando una posizione stack con un offset in grado di migliorare le prestazioni in alcune circostanze diverse.
La parte importante di lock; addl
e xchgl
è il prefisso lock
. E 'implicito per xchgl
. Non c'è davvero nessuna differenza tra i due. Mi piacerebbe guardare a come assemblare e scegliere quello che è più breve (in byte) dal momento che di solito è più veloce per le operazioni equivalenti a 86 (da qui trucchi come xorl eax,eax
)
La presenza di SSE2 è probabilmente solo un proxy per la condizione reale che è in ultima analisi, una funzione di cpuid
. Probabilmente scopre che SSE2 implica l'esistenza di lfence
e la disponibilità di SSE2 è stata controllata / cache in fase di boot. lfence
è richiesto quando è disponibile.