2012-07-02 5 views
32

Mentre guardavo lo stato di implementazione di Clang e g ++ C++ 11 ho notato qualcosa di strano:
supportano gli atomici C++ 11, ma non supportano il modello di memoria C++ 11.
Ho avuto l'impressione che sia necessario il modello di memoria C++ 11 per utilizzare l'atomica. Quindi, qual è esattamente la differenza tra il supporto per l'atomica e il modello di memoria?
La mancanza di supporto del modello di memoria significa che i programmi legali C++ 11 che utilizzano std::atomic<T> non sono coerenti?Come i compilatori C++ supportano C++ 11 atomico, ma non supportano il modello di memoria C++ 11

riferimenti:
http://clang.llvm.org/cxx_status.html
http://gcc.gnu.org/gcc-4.7/cxx0x_status.html

+0

L'atomicità è una delle (tre?) Proprietà del modello di memoria, insieme alla visibilità della memoria e all'ordinamento della memoria. L'atomicità non è un "sinonimo" del modello di memoria. – mloskot

risposta

15

Uno dei problemi è la definizione di "posizione di memoria", che consente (e obbliga il compilatore di supportare) il blocco di diversi membri della struttura da blocchi diversi. C'è un discussion about a RL problem caused by this.

In sostanza il problema è che avendo un struct definita in questo modo:

struct x { 
    long a; 
    unsigned int b1; 
    unsigned int b2:1; 
}; 

il compilatore è libero di implementare la scrittura b2 sovrascrivendo b1 troppo (ea quanto pare, a giudicare dal rapporto, lo fa). Pertanto, i due campi devono essere bloccati come uno. Tuttavia, come conseguenza del modello di memoria C++ 11, questo è vietato (beh, non proprio proibito, ma il compilatore deve garantire aggiornamenti simultanei a b1 e b2 non interferire, potrebbe farlo bloccando o CAS-in ogni tale aggiornamento, beh, la vita è difficile su alcune architetture). Citando dal rapporto:

ho sollevato la questione con i nostri ragazzi del CCG e hanno detto a me che: "C non non fornisce tale garanzia, né è possibile bloccare in modo affidabile diversi campi della struttura con diverse serrature, se condividono regioni di memoria word-size allineate in modo naturale.Il modello di memoria C++ 11 garantisce questo, ma non è implementato né si costruisce il kernel con un compilatore C++ 11 ".

Belle informazioni possono anche essere trovate nel wiki.

+0

tecnico per me, ma questa è una bella informazione: il motivo per cui citiamo il modello di memoria C11/C++ 11 è perché abbiamo in programma di supportare che, probabilmente per GCC 4.8, quando richiesto da alcune opzioni (sia per default quando si sceglie quegli standard ?, o quando richiesto esplicitamente).Questo sarà anche disabilitare alcune ottimizzazioni che vanno bene per le applicazioni a thread singolo, , ma non vanno bene in quei modelli di memoria, non vanno bene in OpenMP o per molti altri programmi con thread . Per esempio. loop store motion se non è garantito che la variabile sia sempre memorizzata nel loop: – NoSenseEtAl

+0

int x; void foo (int j) { int i; per (i = 0; i <100000; i ++) if (i> j) x = i; } non può essere eseguito, perché altrimenti introduce un tmp = x; ... x = tmp; in un codice che altrimenti non toccherebbe la variabile, quindi se alcuni ... altro thread modifica x, potrebbe avere un valore inaspettato. \t Jakub – NoSenseEtAl

+0

cool edit (collegamento è fantastico, spesso mi chiedo dove vivono i ppl che rendono le cose magiche (compilatori)) dal vivo e perché sono così timidi nel condividere le informazioni. :) Il fatto è che la maggior parte delle informazioni di tempo è lì, ma non facilmente googlable :) – NoSenseEtAl

0

non è così tanto che non supportano il modello di memoria, ma che non lo fanno (ancora) supportano l'API in standard per l'interazione con la memoria modello. Quell'API include un numero di mutex.

Tuttavia, sia Clang che GCC sono stati il ​​più consapevoli dei thread possibili senza uno standard formale per qualche tempo. Non devi preoccuparti delle ottimizzazioni che spostano le cose dal lato sbagliato delle operazioni atomiche.

+1

Questa risposta è davvero insoddisfacente; l'elenco delle funzionalità collegate nella domanda * specificamente * parla del modello di memoria (e della sua proposta), non di alcuna API, libreria, mutex e quant'altro. Inoltre, almeno gcc 4.7 * fa * ha i mutex nella libreria. Parlare come "consapevole del thread possibile senza uno standard formale" è solo una cazzata; non significa niente Thread aware non ti dice nulla di ciò che fa il compilatore e c'è sicuramente più di "spostare le cose dalla parte sbagliata delle operazioni atomiche". – jpalecek

+1

@jpalecek mentre sono d'accordo che la risposta non è così bella non c'è bisogno di dire bs. :) E sì, i mutex sono in g ++ dalla versione 4.6 almeno, anche se ricordo male atomic_thread_fence wasnt. :) Non posso controllare ora, (Im on the VS atm). – NoSenseEtAl

+0

@NoSenseEtAl: atomic_thread_fence è in gcc-4.7. Ho usato il termine BS per riferirsi a frasi di marketing vaghe, come "supporto nativo per ...", che sembrano essere importanti, ma dopo un po 'di riflessione, non significano nulla. – jpalecek

11

Suppongo che in questi casi la "Mancanza del modello di memoria" significhi solo che gli ottimizzatori sono stati scritti prima che il modello di memoria C++ 11 sia stato pubblicato e potrebbero ora eseguire ottimizzazioni non valide. È molto difficile e richiede molto tempo per convalidare le ottimizzazioni rispetto al modello di memoria, quindi non è una sorpresa che i team clang/gcc non abbiano ancora finito.

Una mancanza di supporto del modello di memoria significa che i programmi legali C++ 11 che utilizzano std :: atomic arent seq sono coerenti?

Sì, questa è una possibilità. È anche peggio: il compilatore potrebbe introdurre le corse di dati in (secondo lo standard C++ 11) senza gara, ad es. introducendo scritture speculative.

Ad esempio, diversi compilatori C++ utilizzati per eseguire questa ottimizzazione:

for (p = q; p = p -> next; ++p) { 
    if (p -> data > 0) ++count; 
} 

Potrebbe ottenere ottimizzato in:

register int r1 = count; 
for (p = q; p = p -> next; ++p) { 
    if (p -> data > 0) ++r1; 
} 
count = r1; 

Se tutto p->data sono non-negativo, il codice sorgente originale non ha scritto a count, ma il codice ottimizzato lo fa. Questo può introdurre una corsa di dati in un programma altrimenti senza gara, quindi la specifica C++ 11 non consente tali ottimizzazioni. I compilatori esistenti ora devono verificare (e correggere se necessario) tutte le ottimizzazioni.

Vedere Concurrency memory model compiler consequences per dettagli.

+0

sai cos'è divertente? MS dice per VC++ 11: Modello di memoria: N2429 ha reso il Core Language riconoscere l'esistenza del multithreading, ma sembra che non ci sia nulla da fare per un'implementazione del compilatore (almeno uno che supportava già il multithreading). Quindi è N/A sul tavolo. – NoSenseEtAl

+0

@NoSenseEtAl: Questa è solo una parte molto specifica dell'intero modello di memoria. La stessa tabella ha anche "Data-dependency ordering" (N2664) che ha richiesto il lavoro. Normalmente, lo considereremmo parte del modello di memoria C++ 11. – MSalters

+0

Ok .. quindi sono pigri. È quello che pensavo. –