2013-12-12 14 views
41

Dire che ho un grande array e voglio elaborare il contenuto con più thread. Se delegare ciascun thread a una sezione specifica, garantendo l'assenza di sovrapposizioni, elimina qualsiasi necessità di blocco, presupponendo che i thread non accedano a nessun'altra memoria al di fuori dell'array?È possibile evitare il blocco garantendo che più thread non accedano alla stessa memoria?

Qualcosa di simile (pseudo-codice):

global array[9000000]; 

do_something(chunk) { 
    for (i = chunk.start; i < chunk.end; i++) 
     //do something with array 
} 

main() { 
    chunk1 = {start: 0, end: 5000000}; 
    chunk2 = {start: 5000000, end: 9000000}; 

    start_thread(thread1, do_something(chunk1)); 
    start_thread(thread2, do_something(chunk2)); 

    wait_for_join(thread1); 
    wait_for_join(thread2); 
    //do something else with the altered array 
} 

risposta

31

In un compilatore C++ 11 conforme questo è sicuro [intro.memory] (§1.7):

posizione

una memoria è o un oggetto di tipo scalare o un massimo sequenza di bit adiacenti campi tutti aventi larghezza diversa da zero. [...] Due thread di esecuzione (1.10) possono aggiornare e accedere a posizioni separate della memoria senza interferire l'uno con l'altro.

C11 fornisce garanzie identiche (usano anche la stessa formulazione) nel §3.14.

In un compilatore C++ 03 non è garantito il funzionamento dello standard, ma potrebbe comunque funzionare se il compilatore fornisce garanzie analoghe come estensione.

+4

Esiste un compilatore che non fornisce questo comportamento in C++ 03? Anche se, sebbene non fosse formalizzato, era una caratteristica piuttosto comune sull'hardware moderno ... – Errata

+0

Non ha nulla a che fare con l'hardware. Diciamo che voglio copiare 15 byte da src a dst. Il compilatore legge il byte in dst che dovrebbe rimanere invariato, copia 16 byte usando un registro vettoriale, scrive il byte indietro. Tutto mentre un altro thread guardava quel byte. Il tuo codice sorgente non ha accesso allo stesso codice, ma il codice compilato lo ha fatto. C11 e C++ 11 non lo faranno. Ricorda che diversi campi di bit possono contare come la stessa memoria. – gnasher729

18

Sì: se è possibile garantire che due thread non accedano allo stesso elemento, non è necessaria alcuna ulteriore sincronizzazione.

Esiste solo un conflitto (e quindi una potenziale corsa di dati) se due thread accedono alla stessa posizione di memoria (con almeno uno di essi che lo modifica) senza sincronizzazione.

(NOTA: questa risposta è basata sul modello di memoria C++ 11. Ho appena notato che stai anche chiedendo di una seconda lingua, credo che C11 specifichi un modello di memoria molto simile, ma puo ' t di sicuro la risposta è valida anche per C. Per le versioni precedenti di entrambe le lingue, thread-safety era dipendente dall'implementazione.)

+0

"... se due thread accedono alla stessa posizione di memoria senza sincronizzazione" e uno di questi accessi è una scrittura. –

+0

@AndrewDurward: Effettivamente, ho semplificato leggermente. Il punto rilevante per questa domanda è che se non accedono allo stesso luogo, non può esserci conflitto. –

+1

Se i "blocchi" si sovrappongono sulle linee della cache, è possibile che si verifichino prestazioni ridotte se due thread stanno elaborando elementi sulla stessa linea della cache da diversi core della CPU. Per prestazioni ottimali, rendere le dimensioni del blocco un multiplo della dimensione della linea della cache della piattaforma e correttamente allineate. Ma a prescindere, non è necessaria alcuna sincronizzazione. –

4

Sì.

Non è nemmeno necessario garantire che non ci siano due thread l'accesso allo la stessa posizione di memoria. Tutto ciò che devi garantire è che nessun thread singolo modifica qualsiasi posizione a cui un altro accede (indipendentemente dal fatto che ciò significhi leggere o scrivere).

Dato sia senza l'accesso simultaneo a tutti o sola lettura l'accesso simultaneo, siete pronti ad andare senza bloccare.

+1

Un singolo modificatore non è sufficiente per garantire la sicurezza. Un lettore, leggendo i dati mentre è scritto, può ottenere risultati non validi. – ugoren

+0

@ugoren: RIght. Questo è il motivo per cui ho detto "** no ** scrittura a thread singolo". Un lettore può ottenere risultati incoerenti (anche se non "confusi", solo incoerenti, a causa di come funzionano tutte le moderne CPU, ma di solito è abbastanza brutto) con uno scrittore. In altre parole, qualsiasi numero di lettori è sicuro, ma nessuno scrittore (nella stessa posizione di un lettore). – Damon

3

Importante problema di prestazioni!

Destra, non è necessario il blocco esplicito, poiché, come detto da altri, nessuna posizione di memoria è condivisa.

Ma è possibile attivare la sincronizzazione hardware implicita , poiché è probabile che i blocchi arbitrari inducano le linee della cache a essere condivise (non molto con le cifre utilizzate nell'esempio, tuttavia). È noto come false sharing.approccio livello

superiore come OpenMP risolve questo tipo di problema:

  • allineamento blocchi (e filati numeri) sono sintonizzati secondo hardware sottostante.
  • fornisce un controllo migliore sul pool di thread, ad esempio ammortizzando il costo delle istanze di thread.
  • è più facile da scrivere e in realtà meno invadente.