2011-02-08 4 views
10

So che volatile non impone atomicity su int, ad esempio, ma lo fa se si accede a un singolo byte? La semantica richiede che le scritture e le letture vengano sempre dalla memoria se ricordo bene.È volatile un modo corretto per rendere atomico un singolo byte in C/C++?

O in altre parole: le CPU leggono e scrivono byte sempre atomicamente?

+0

È necessario C++ 0x atomica –

+2

C1X fornisce anche le operazioni atomiche – Christoph

+0

Sì, ho pensato che anche. Mi stavo solo chiedendo, perché il codice in questo articolo di Wikipedia: http://en.wikipedia.org/wiki/Busy_waiting è semplicemente sbagliato. –

risposta

20

Non solo lo standard non dice nulla di atomicità, ma probabilmente stai anche facendo la domanda sbagliata.

Le CPU in genere leggono e scrivono singoli byte atomicamente. Il problema si presenta perché quando si hanno più core, non tutti i core vedranno il byte come se fosse stato scritto nello stesso momento. In realtà, potrebbe essere un bel po 'di tempo (in termini di CPU, migliaia o milioni di istruzioni (ovvero microsecondi o forse millisecondi)) prima che tutti i core abbiano visto la scrittura.

Quindi, sono necessarie le operazioni atomiche C++ 0x un po 'anonime. Usano le istruzioni della CPU che assicurano che l'ordine delle cose non venga incasinato e che quando altri core guardano il valore che hai scritto dopo averlo scritto, vedono il nuovo valore, non quello vecchio. Il loro lavoro non è esattamente l'atomicità delle operazioni, ma si assicura anche che si verifichino le opportune fasi di sincronizzazione.

+0

+1 per menzionare la sincronizzazione. L'OP deve trovare metodi per proteggere le variabili da altri core (CPU) che accedono ad esso –

+0

Grazie per aver confermato che le CPU non scambiano le linee della cache quando non si utilizza un modificatore di istruzioni atomiche –

+0

È possibile fornire un collegamento a queste operazioni atomiche di C++ 0x ? Ho trovato 'std :: lock_guard',' std :: mutex' e così via, ma sono questi la cosa giusta? –

4

Lo standard non dice nulla di atomicità.

3

La volatile parola chiave viene usata per indicare che una variabile (int, char, o altro) può essere assegnato un valore da un fonte esterna imprevedibile. Questo di solito impedisce al compilatore di ottimizzare la variabile.

Per atomico è necessario controllare la documentazione del compilatore per verificare se fornisce assistenza o dichiarazioni o direttive.

3

Risposta breve: Non utilizzare volatili per garantire l'atomicità.

Risposta lunga Si potrebbe pensare che dal momento che le CPU di gestire le parole in una singola istruzione, le operazioni di semplice parola sono intrinsecamente thread-safe. L'idea di utilizzare volatile è quella di assicurare che il compilatore non faccia alcuna ipotesi sul valore contenuto nella variabile condivisa.

Su moderne macchine multiprocessore, questa ipotesi è errata. Dato che diversi core del processore avranno normalmente una propria cache, potrebbero verificarsi circostanze in cui le letture e le scritture nella memoria principale vengono riordinate e il codice finisce per non comportarsi come previsto.

Per questo motivo sempre utilizzare blocchi come mutex o sezioni critiche quando la memoria di accesso condivisa tra thread. Sono sorprendentemente economici quando non ci sono contese (normalmente non è necessario effettuare una chiamata di sistema) e faranno la cosa giusta.

In genere impediscono le letture e le scritture fuori ordine richiamando un'istruzione Data Memory Barrier (DMB su ARM) che garantisce che le letture e le scritture avvengano nell'ordine corretto. Guarda here per maggiori dettagli.

L'altro problema con volatile è che impedirà al compilatore di effettuare ottimizzazioni anche se perfettamente a posto.

3

Su qualsiasi CPU sana, la lettura e la scrittura di qualsiasi tipo allineato, di dimensioni o di caratteri inferiori è atomico. Questo non è il problema. I problemi sono:

  • Solo perché le letture e le scritture sono atomiche, non segue che le sequenze di lettura/modifica/scrittura siano atomiche. Nel linguaggio C, x++ è concettualmente un ciclo di lettura/modifica/scrittura. Non è possibile controllare se il compilatore genera un incremento atomico e, in generale, non lo farà.
  • Problemi di sincronizzazione della cache. Nelle architetture half-crap (praticamente tutto non-x86), l'hardware è troppo stupido per garantire che la visualizzazione della memoria di ciascuna CPU rifletta l'ordine in cui le scritture hanno avuto luogo. Ad esempio se cpu 0 scrive su indirizzi A poi B, è possibile che cpu 1 veda l'aggiornamento all'indirizzo B ma non l'aggiornamento all'indirizzo A. Sono necessari speciali opcodi di barriera/barriera di memoria per risolvere questo problema, e il compilatore non genererà loro per te

Il secondo punto importante solo sui sistemi SMP/multicore, quindi se sei felice limitando te stesso per single-core, si può ignorare, e quindi pianura legge e scrive sarà atomica in C su qualsiasi sana architettura della CPU. Ma non puoi fare molto utile con questo. (Per esempio, l'unico modo è possibile implementare un semplice blocco in questo modo comporta O(n) spazio, dove n è il numero di thread.)

+0

Ho avuto l'impressione che i problemi di sincronizzazione della cache siano sicuramente un problema su x86 e su altre architetture? Ricordo vagamente di aver letto qualcosa che diceva fondamentalmente se i 2 core condividono la cache (es .: un core 2 duo), quindi sarai OK, ma non appena avrai più di un processore fisico, o core senza cache condivisa (es: un pentium D o core 2 quad), quindi è necessario preoccuparsi della sincronizzazione della cache –

+0

Per quanto ne so, ogni x86 mai creato tranne alcuni economici imbrogli Cyrix (iirc) ha ordinato rigorosamente scritture di memoria, anche tra cpu fisiche separate. –

+1

Le altre architetture non sono esattamente "stupide", ma piuttosto ottimizzate per l'esecuzione di centinaia di core in parallelo. Un modo per aumentare le prestazioni è non sprecare tempo per sincronizzare le cache se non richiesto esplicitamente. L'x86 è più semplice da utilizzare, ma non è adatto a sistemi di memoria condivisi. –