Il compilatore è obbligato solo a emulare il comportamento osservabile di un programma, quindi se un riordino non violerebbe tale principio, allora sarebbe consentito. Supponendo che il comportamento sia ben definito, se il tuo programma contiene undefined behavior come una corsa di dati, il comportamento del programma sarà imprevedibile e, come commentato, richiederebbe l'uso di una qualche forma di sincronizzazione per proteggere la sezione critica.
un utile riferimento
Un interessante articolo che copre questo è Memory Ordering at Compile Time e dice:
La regola cardinale di riordino della memoria, che è universalmente seguita dagli sviluppatori del compilatore e fornitori di CPU, può essere formulato come segue:
Non modificare il comportamento di un programma a thread singolo.
Un esempio
L'articolo fornisce un semplice programma in cui possiamo vedere questo riordino:
int A, B; // Note: static storage duration so initialized to zero
void foo()
{
A = B + 1;
B = 0;
}
e spettacoli a livelli di ottimizzazione più elevati B = 0
è fatto prima A = B + 1
, e possiamo riprodurre questo risultato utilizzando godbolt, che durante l'utilizzo di -O3
produce il seguente g (see it live):
movl $0, B(%rip) #, B
addl $1, %eax #, D.1624
Perché?
Perché il compilatore riordina? L'articolo spiega è esattamente lo stesso motivo il processore fa, a causa della complessità dell'architettura:
Come accennato all'inizio, il compilatore modifica dell'ordine di memoria interazioni per la stessa ragione che il processore lo fa - ottimizzazione delle prestazioni. Tali ottimizzazioni sono una conseguenza diretta della complessità della CPU moderna.
Standards
Nel progetto C++ standard, questo è coperto nella sezione 1.9
esecuzione programma che dice (sottolineatura mia andare avanti):
Le descrizioni semantiche di questo Lo standard internazionale definisce una macchina astratta non deterministica con parametri . Questo standard internazionale non pone alcun requisito sulla struttura delle implementazioni conformi . In particolare, non è necessario copiare o emulare la struttura della macchina astratta. Piuttosto, le implementazioni conformi sono necessarie per emulare (solo) il comportamento osservabile della macchina astratta come spiegato di seguito.
nota 5
ci dice questo è noto anche come il come-se regola:
Questa disposizione è talvolta chiamato il “come-se” regola, perché un l'implementazione è libera da qualsiasi requisito di questo Standard internazionale finché il risultato è come se il requisito era stato rispettato, per quanto è possibile determinare dal comportamento osservabile del programma.Ad esempio, un'implementazione effettiva necessita di non valuta parte di un'espressione se è possibile dedurre che il suo valore è non utilizzato e che non viene prodotto alcun effetto collaterale sul comportamento osservabile di .
il progetto di C99 e progetto di standard C11 copre questo nella sezione 5.1.2.3
esecuzione programma anche se dobbiamo andare all'indice di vedere che è chiamato il come-se regola nello standard C così:
as-se di regola, 5.1.2.3
Aggiornamento su considerazioni di blocco-free
L'articolo An Introduction to Lock-Free Programming tratta questo argomento bene e per i PO preoccupazioni sulla tavolo lock-meno condiviso-hash implementazione di questa sezione è probabilmente il più rilevante:
di memoria per l'ordinazione
Come il diagramma di flusso suggerisce, ogni volta che si esegue la programmazione senza blocco per il multicore (o qualsiasi symmetric multiprocessor), e il proprio ambiente non garantisce la coerenza sequenziale , è necessario considerare come impedire memory reordering.
Su architetture di oggi, gli strumenti per far rispettare corretta memoria ordinare rientrano generalmente in tre categorie, che impediscono sia compiler reordering e processor reordering:
- Una sincronizzazione o recinzione di istruzioni leggero, che parlerò in future posts ;
- Un'istruzione di memoria piena, che ho demonstrated previously;
- Operazioni di memoria che forniscono semantica di acquisizione o rilascio.
semantica Acquisire impediscono riordino memoria delle operazioni che seguono al fine programma, e rilasciare semantica impediscono memoria riordino delle operazioni precedenti esso. Queste semantiche sono particolarmente adatte allo nei casi in cui esiste una relazione produttore/consumatore, in cui un thread pubblica alcune informazioni e l'altro lo legge. Mi parlerò anche di in un prossimo post.
Anche se il compilatore è stato garantito per generare il codice in tale ordine, la CPU stessa esegue l'esecuzione dell'ordine. – KitsuneYMG
Non solo i compilatori sono autorizzati a farlo, le CPU possono farlo, i controller di memoria sono autorizzati a farlo, le cache sono autorizzate a farlo e così via. –
Ho appena aggiunto un esempio interessante che mostra questo praticamente e non solo teoricamente. –