Qual è la differenza tra atomic e critical in OpenMP?


Risposte:


173

L'effetto su g_qCount è lo stesso, ma ciò che viene fatto è diverso.

Una sezione critica di OpenMP è completamente generale: può circondare qualsiasi blocco arbitrario di codice. Tuttavia, si paga per questa generalità incorrendo in un overhead significativo ogni volta che un thread entra ed esce dalla sezione critica (oltre al costo intrinseco della serializzazione).

(Inoltre, in OpenMP tutte le sezioni critiche senza nome sono considerate identiche (se preferisci, c'è solo un blocco per tutte le sezioni critiche senza nome), in modo che se un thread è in una sezione critica [senza nome] come sopra, nessun thread può entrare [sezione critica senza nome]. Come puoi immaginare, puoi aggirare questo problema utilizzando sezioni critiche con nome).

Un'operazione atomica ha un sovraccarico molto inferiore. Dove disponibile, sfrutta l'hardware fornendo (diciamo) un'operazione di incremento atomico; in tal caso non è necessario alcun blocco / sblocco per entrare / uscire dalla riga di codice, esegue solo l'incremento atomico con cui l'hardware dice che non è possibile interferire.

I lati positivi sono che l'overhead è molto più basso e che un thread in un'operazione atomica non blocca alcuna (diversa) operazione atomica che sta per verificarsi. Lo svantaggio è l'insieme limitato di operazioni supportate da atomic.

Ovviamente, in entrambi i casi, dovrai sostenere il costo della serializzazione.


5
"potresti perdere la portabilità" - Non sono sicuro che sia vero. Lo standard (versione 2.0) specifica quali operazioni atomiche sono consentite (fondamentalmente cose come ++e *=) e che se non sono supportate nell'hardware, potrebbero essere sostituite da criticalsezioni.
Dan R

@ DanRoche: Sì, hai ragione. Non credo che questa affermazione sia mai stata corretta, la correggerò ora.
Jonathan Dursi

Qualche giorno fa ho seguito un tutorial su OpenMP e, da quanto ho capito, c'è una differenza nei due diversi codici. Questo è il risultato che può differire perché la sezione critica assicura che l'istruzione venga eseguita da un thread una volta, tuttavia è possibile che l'istruzione: g_qCount = g_qCount + 1; per il thread 1 memorizza semplicemente il risultato g_qCount solo nel writebuffer non nella memoria RAM, e quando il thread 2 recupera il valore g_qCount, legge semplicemente quello nella RAM, non nel writebuffer. L'istruzione atomica assicura che l'istruzione abbia scaricato i dati in memoria
Giox79

31

In OpenMP, tutte le sezioni critiche senza nome si escludono a vicenda.

La differenza più importante tra critico e atomico è che atomic può proteggere solo un singolo incarico e puoi usarlo con operatori specifici.


13
Sarebbe stato meglio un commento (o una modifica) della risposta precedente.
kynan

20

Sezione critica:

  • Assicura la serializzazione dei blocchi di codice.
  • Può essere esteso per serializzare gruppi di blocchi con un uso appropriato del tag "name".

  • Più lentamente!

Operazione atomica:

  • È molto più veloce!

  • Assicura solo la serializzazione di una particolare operazione.


9
Ma questa risposta è molto leggibile e sarebbe un ottimo riassunto della prima risposta
Michał Miszczyszyn

7

Il modo più veloce non è né critico né atomico. Approssimativamente, l'aggiunta con sezione critica è 200 volte più costosa dell'aggiunta semplice, l'aggiunta atomica è 25 volte più costosa dell'aggiunta semplice.

L'opzione più veloce (non sempre applicabile) è quella di dare a ogni thread il proprio contatore e di effettuare un'operazione di riduzione quando è necessaria la somma totale.


2
Non sono d'accordo con tutti i numeri che hai menzionato nella tua spiegazione. Supponendo x86_64, l'operazione atomica avrà un sovraccarico di alcuni cicli (sincronizzazione di una riga della cache) sul costo di circa un ciclo. Se avessi un costo di "condivisione reale" altrimenti, il sovraccarico sarebbe nullo. Una sezione critica sostiene il costo di una serratura. A seconda che il blocco sia già stato attivato o meno, l'overhead è di circa 2 istruzioni atomiche OPPURE due esecuzioni dello scheduler e il tempo di sospensione, che di solito sarà significativamente più di 200x.
Klaas van Gend

6

I limiti di atomicsono importanti. Dovrebbero essere dettagliati sul specifiche di OpenMP . MSDN offre un breve cheat sheet in quanto non sarei sorpreso se questo non cambierà. (Visual Studio 2012 ha un'implementazione di OpenMP da marzo 2002.) Per citare MSDN:

La dichiarazione dell'espressione deve avere una delle seguenti forme:

xbinop =expr

x++

++x

x--

--x

Nelle espressioni precedenti: xè lvalueun'espressione di tipo scalare. exprè un'espressione con tipo scalare e non fa riferimento all'oggetto designato da x. binop non è un operatore sovraccarico ed è uno dei +, *, -, /, &, ^, |, <<, o >>.

Raccomando di usare atomicquando è possibile e altrimenti denominate sezioni critiche. Denominarli è importante; eviterai il debug dei mal di testa in questo modo.


1
Questo non è tutto, abbiamo altre direttive atomiche avanzate come: #pragma omp aromic update (o read, upate, write, capture) quindi ci permette di avere qualche altra affermazione benefica
pooria

1

Già ottime spiegazioni qui. Tuttavia, possiamo immergerci un po 'più a fondo. Per comprendere la differenza fondamentale tra i concetti di sezione atomica e critica in OpenMP, dobbiamo prima comprendere il concetto di blocco . Rivediamo perché è necessario utilizzare i lucchetti .

Un programma parallelo viene eseguito da più thread. I risultati deterministici si verificheranno se e solo se eseguiamo la sincronizzazione tra questi thread. Ovviamente, la sincronizzazione tra i thread non è sempre richiesta. Ci riferiamo a quei casi in cui è necessaria la sincronizzazione .

Per sincronizzare i thread in un programma multi-thread, useremo lock . Quando l'accesso deve essere limitato da un solo thread alla volta, entrano in gioco i lock . L' implementazione del concetto di blocco può variare da processore a processore. Scopriamo insieme come può funzionare una semplice serratura da un punto di vista algoritmico.

1. Define a variable called lock.
2. For each thread:
   2.1. Read the lock.
   2.2. If lock == 0, lock = 1 and goto 3    // Try to grab the lock
       Else goto 2.1    // Wait until the lock is released
3. Do something...
4. lock = 0    // Release the lock

L'algoritmo dato può essere implementato nel linguaggio hardware come segue. Assumeremo un singolo processore e analizzeremo il comportamento dei blocchi in questo. Per questa pratica, supponiamo uno dei seguenti processori: MIPS , Alpha , ARM o Power .

try:    LW R1, lock
        BNEZ R1, try
        ADDI R1, R1, #1
        SW R1, lock

Questo programma sembra essere OK, ma non lo è. Il codice precedente soffre del problema precedente; sincronizzazione . Troviamo il problema. Supponiamo che il valore iniziale di lock sia zero. Se due thread eseguono questo codice, uno potrebbe raggiungere il SW R1, bloccarsi prima che l'altro legga la variabile lock . Quindi, entrambi pensano che il lucchetto sia libero. Per risolvere questo problema, viene fornita un'altra istruzione piuttosto che semplici LW e SW . Si chiama istruzione di lettura-modifica-scrittura . È un'istruzione complessa (composta da sottoistruzioni) che assicura che la procedura di acquisizione del blocco venga eseguita da un solothread alla volta. La differenza di lettura-modifica-scrittura rispetto alle semplici istruzioni di lettura e scrittura è che utilizza un modo diverso di caricamento e archiviazione . Utilizza LL (Load Linked) per caricare la variabile di blocco e SC (Store Conditional) per scrivere nella variabile di blocco. Un registro di collegamento aggiuntivo viene utilizzato per garantire che la procedura di acquisizione del blocco venga eseguita da un singolo thread. L'algoritmo è fornito di seguito.

1. Define a variable called lock.
2. For each thread:
   2.1. Read the lock and put the address of lock variable inside the Link Register.
   2.2. If (lock == 0) and (&lock == Link Register), lock = 1 and reset the Link Register then goto 3    // Try to grab the lock
       Else goto 2.1    // Wait until the lock is released
3. Do something...
4. lock = 0    // Release the lock

Quando il registro di collegamento viene ripristinato, se un altro thread ha assunto che il blocco sia libero, non sarà in grado di scrivere nuovamente il valore incrementato nel blocco. Pertanto, viene acquisita la concorrenza di accesso alla variabile lock .

La differenza fondamentale tra critico e atomico deriva dall'idea che:

Perché usare i lock (una nuova variabile) mentre possiamo usare la variabile effettiva (su cui stiamo eseguendo un'operazione), come una variabile di lock?

L'uso di una nuova variabile per i blocchi porterà a una sezione critica , mentre l'uso della variabile effettiva come blocco porterà al concetto atomico . La sezione critica è utile quando eseguiamo molti calcoli (più di una riga) sulla variabile effettiva. Questo perché, se il risultato di quei calcoli non viene scritto sulla variabile effettiva, l'intera procedura dovrebbe essere ripetuta per calcolare i risultati. Ciò può portare a prestazioni scadenti rispetto all'attesa del rilascio del blocco prima di entrare in una regione altamente computazionale. Pertanto, si consiglia di utilizzare la direttiva atomic ogni volta che si desidera eseguire un singolo calcolo (x ++, x--, ++ x, --x, ecc.) E utilizzare una direttiva critica quando una regione più complessa dal punto di vista computazionale viene eseguita dalla sezione intensiva.


-5

atomic è relativamente efficiente in termini di prestazioni quando è necessario abilitare l'esclusione reciproca solo per una singola istruzione simile non è vero per omp critical.


13
Questa non è altro che una riformulazione mal formulata della risposta accettata senza spiegazione.
High Performance Mark

-5

atomic è una sezione critica di una singola istruzione, ovvero si blocca per l'esecuzione di un'istruzione

la sezione critica è un blocco su un blocco di codice

Un buon compilatore tradurrà il tuo secondo codice nello stesso modo in cui fa il primo


È solo sbagliato. Per favore, non parlare di cose che non capisci.
jcsahnwaldt Ripristina Monica il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.