Domanda

L'altra settimana, ho scritto un po classe filo e un tubo messaggio unidirezionale per consentire la comunicazione tra i thread (due tubi per discussione, ovviamente, per le comunicazioni bidirezionali). Tutto ha funzionato bene sul mio Athlon 64 X2, ma mi chiedevo se mi piacerebbe correre in qualche problema se entrambi i fili stavano guardando la stessa variabile ed il valore memorizzato nella cache locale per questa variabile su ogni core ero fuori sincrono.

So che il volatili parola chiave costringerà una variabile per rinfrescarsi dalla memoria, ma c'è un modo su processori multicore x86 per forzare le cache di tutti i core per la sincronizzazione? E 'questo qualcosa che ho bisogno di preoccuparsi, o la volontà volatili e il corretto uso dei meccanismi di bloccaggio leggeri (stavo usando _InterlockedExchange per impostare le mie variabili tubi volatili) gestire tutti i casi in cui voglio scrivere "bloccare libero" codice per le CPU multicore x86?

Sono già a conoscenza e hanno usato le sezioni critiche, mutex, eventi, e così via. Sono per lo più chiedo se ci sono intrinseci x86 che io non sono a conoscenza di quale forza o può essere utilizzato per far rispettare la coerenza della cache.

È stato utile?

Soluzione

volatile obbliga solo il codice per ri-leggere il valore, non può controllare dove il valore viene letto da. Se il valore è stato recentemente letto dal vostro codice, allora probabilmente sarà nella cache, nel qual caso volatili costringerà ad essere ri-letta dalla cache, non dalla memoria.

Non ci sono un sacco di istruzioni coerenza della cache in x86. Ci sono le istruzioni prefetch come prefetchnta , ma che non influenza la semantica di memoria-ordinazione. Ha usato per essere attuate portando il valore di cache L1 L2 senza inquinare, ma le cose sono più complicate per Intel design moderno con una grande condiviso inclusiva cache L3.

CPU x86 utilizzano una variazione sul protocollo MESI (MESIF per Intel, MOESI per AMD) per mantenere le loro cache coerenti tra loro (comprese le cache L1 private di diversi nuclei). Un nucleo che vuole scrivere una riga di cache deve costringere altri nuclei di invalidare la loro copia di esso prima che possa cambiare la propria copia da condivisa a stato modificato.


Non è necessario alcuna istruzione recinzione (come MFENCE) per produrre i dati in un thread e consumare in un altro su x86, perché i carichi x86 / negozi hanno acquisire semantica / rilascio built-in. Si ha bisogno MFENCE (barriera completa) per ottenere la consistenza sequenziale. (Una versione precedente di questa risposta ha suggerito che era necessario clflush, che non è corretto).

Si ha bisogno di evitare che compile-time riordino , perché modello di memoria C ++ s 'è debolmente-ordinato. volatile è un vecchio, brutto modo di fare questo; C ++ 11 std :: atomica è un modo molto migliore di scrivere codice senza blocchi.

Altri suggerimenti

coerenza cache è garantito tra nuclei a causa del protocollo MESI impiegato da processori x86. Hai solo bisogno di preoccuparsi per la coerenza di memoria quando si tratta di hardware esterno che possono accedere alla memoria mentre i dati è ancora la scelta del sito in cache core. Non sembra che sia il tuo caso qui, però, dal momento che il testo suggerisce che stai programmazione in spazio utente.

Non è necessario preoccuparsi di coerenza della cache. L'hardware si prenderà cura di questo. Quello che potrebbe essere necessario preoccuparsi di problemi di prestazioni è dovuta a quella coerenza della cache.

Se nucleo # 1 scrive a una variabile, che invalida tutte le altre copie della linea di cache in altri core (perché deve ottenere esclusiva titolarità della linea di cache prima di commettere il negozio). Quando nucleo # 2 si legge che lo stesso variabile, mancherà nella cache (a meno di base # 1 ha già scritto indietro fino ad un livello di condivisione di cache).

Poiché un'intera riga di cache (64 byte) deve essere letta dalla memoria (o scritta torna alla cache condivisa e poi letto dal nucleo # 2), si avrà un certo costo prestazioni. In questo caso, è inevitabile. Questo è il comportamento desiderato.


Il problema è che quando si dispone di più variabili nella stessa linea di cache, il processore potrebbe trascorrere più tempo mantenendo le cache in sincronia anche se i core sono la lettura / scrittura di variabili diverse all'interno della stessa linea di cache.

Tale costo può essere evitato facendo in modo che queste variabili non sono nella stessa linea di cache. Questo effetto è noto come False Condivisione dal momento che si stanno costringendo i processori per sincronizzare i valori degli oggetti che non sono in realtà condivisi tra i thread.

volatile non lo farà. In C ++, volatile colpisce solo ciò ottimizzazioni di compilatore come la memorizzazione di una variabile in un registro invece di memoria, o rimuoverlo completamente.

Non hai specificato quale compilatore che si sta utilizzando, ma se siete su Windows, dare un'occhiata a questo articolo qui . Anche dare un'occhiata alle funzioni ynchronization la disposizione s qui . Si potrebbe desiderare di notare che in volatile generale non è sufficiente per fare quello che vuoi che faccia, ma sotto VC 2005 e il 2008, ci sono la semantica non standard aggiunti ad esso che aggiunge implicita barriere di memoria intorno leggere e scrive.

Se si vuole che le cose siano portatile, si sta andando ad avere una strada molto più difficile di fronte a voi.

C'è una serie di articoli che spiegano moderne architetture di memoria qui , tra cui < a href = "http://duartes.org/gustavo/blog/post/intel-cpu-caches" rel = "nofollow noreferrer"> Intel Core2 memorizza nella cache molti argomenti di architettura più moderni e.

Gli articoli sono molto leggibile e ben illustrata. Buon divertimento!

Ci sono diverse sotto-domande nella vostra domanda così li risponderò al meglio delle mie conoscenze.

  1. Attualmente v'è alcun modo portatile di attuare interazioni senza blocchi in C ++. La proposta C ++ 0x risolve questo problema introducendo la libreria Atomics.
  2. volatile non è garantito per fornire atomicità su un multicore e la sua attuazione è specifico del fornitore.
  3. Nella x86, non è necessario fare nulla di speciale, tranne che di dichiarazione delle variabili condivise come volatile per prevenire alcune ottimizzazioni del compilatore che può rompere il codice multithread. Volatile dice al compilatore di non memorizzare nella cache i valori.
  4. Ci sono alcuni algoritmi (Dekker, per esempio) che non funzionano anche su un sistema x86 con le variabili volatili.
  5. A meno che non si sa per certo che il passaggio l'accesso ai dati tra i thread è un importante collo di bottiglia nel vostro programma, stare lontano da soluzioni senza blocchi. Utilizzare i dati di passaggio per valore o serrature.

Il seguente è un buon articolo in riferimento a utilizzare volatile w / programmi filettati.

volatile quasi inutile per Multi-Threaded Programmazione .

Herb Sutter sembrava semplicemente suggeriscono che ogni due variabili devono risiedere su linee di cache separate. Lo fa nella sua coda in concomitanza con imbottitura tra le serrature e puntatori dei nodi.

Edit: Se si utilizza il compilatore Intel o GCC, è possibile utilizzare il noreferrer builtins atomiche , che sembrano fare del loro meglio per prevenire la cache quando possibile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top