Quelle est la différence entre atomique et critique dans OpenMP?


111

Quelle est la différence entre atomique et critique dans OpenMP?

je peux le faire

#pragma omp atomic
g_qCount++;

mais n'est-ce pas la même chose que

#pragma omp critical
g_qCount++;

?

Réponses:


173

L'effet sur g_qCount est le même, mais ce qui est fait est différent.

Une section critique d'OpenMP est complètement générale - elle peut entourer n'importe quel bloc de code arbitraire. Cependant, vous payez pour cette généralité en encourant des frais généraux importants chaque fois qu'un thread entre et sort de la section critique (en plus du coût inhérent de la sérialisation).

(De plus, dans OpenMP, toutes les sections critiques sans nom sont considérées comme identiques (si vous préférez, il n'y a qu'un seul verrou pour toutes les sections critiques sans nom), de sorte que si un thread est dans une section critique [sans nom] comme ci-dessus, aucun thread ne peut entrer dans aucun [sans nom] section critique. Comme vous pouvez le deviner, vous pouvez contourner ce problème en utilisant des sections critiques nommées).

Une opération atomique a une surcharge beaucoup plus faible. Lorsqu'il est disponible, il tire parti du matériel fournissant (par exemple) une opération d'incrémentation atomique; dans ce cas, il n'y a pas de verrouillage / déverrouillage nécessaire pour entrer / sortir de la ligne de code, il fait juste l'incrément atomique avec lequel le matériel vous dit que vous ne pouvez pas être interféré.

Les avantages sont que la surcharge est beaucoup plus faible, et un thread étant dans une opération atomique ne bloque aucune opération atomique (différente) sur le point de se produire. L'inconvénient est l'ensemble restreint d'opérations prises en charge par atomic.

Bien sûr, dans les deux cas, vous engagez le coût de la sérialisation.


5
"vous pourriez perdre la portabilité" - je ne suis pas sûr que ce soit vrai. La norme (version 2.0) spécifie quelles opérations atomiques sont autorisées (essentiellement des choses comme ++et *=) et que si elles ne sont pas prises en charge par le matériel, elles peuvent être remplacées par des criticalsections.
Dan R

@DanRoche: Oui, vous avez tout à fait raison. Je ne pense pas que cette déclaration ait jamais été correcte, je vais la corriger maintenant.
Jonathan Dursi le

Il y a quelques jours, j'ai suivi un tutoriel OpenMP, et d'après ce que j'ai compris, il y a une différence entre les deux codes différents. C'est le résultat peut différer parce que la section critique assure que l'instruction est exécutée par un thread à la fois, cependant il est possible que l'instruction: g_qCount = g_qCount + 1; pour le thread 1 stocke simplement le résultat de g_qCount uniquement dans le tampon d'écriture et non dans la mémoire RAM, et lorsque le thread 2 récupère la valeur g_qCount, il lit simplement celle dans la RAM, pas dans le tampon d'écriture. L'instruction atomique assure que l'instruction a vidé les données en mémoire
Giox79

31

Dans OpenMP, toutes les sections critiques sans nom s'excluent mutuellement.

La différence la plus importante entre critique et atomic est qu'atomic ne peut protéger qu'une seule affectation et que vous pouvez l'utiliser avec des opérateurs spécifiques.


13
Cela aurait mieux été un commentaire (ou une modification) de la réponse précédente.
kynan

20

Section critique:

  • Assure la sérialisation des blocs de code.
  • Peut être étendu pour sérialiser des groupes de blocs en utilisant correctement la balise "name".

  • Ralentissez!

Fonctionnement atomique:

  • Est beaucoup plus rapide!

  • Assure uniquement la sérialisation d'une opération particulière.


9
Mais cette réponse est très lisible et serait un excellent résumé de la première réponse
Michał Miszczyszyn

7

Le moyen le plus rapide n'est ni critique ni atomique. Approximativement, l'addition avec section critique est 200 fois plus chère qu'une addition simple, l'addition atomique est 25 fois plus chère qu'une addition simple.

L'option la plus rapide (pas toujours applicable) est de donner à chaque thread son propre compteur et d'effectuer une opération de réduction lorsque vous avez besoin de la somme totale.


2
Je ne suis pas d'accord avec tous les chiffres que vous mentionnez dans votre explication. En supposant x86_64, l'opération atomique aura une surcharge de quelques cycles (synchronisation d'une ligne de cache) sur le coût d'environ un cycle. Si vous aviez un coût de «vrai partage» autrement, le surcoût est nul. Une section critique entraîne le coût d'une serrure. Selon que le verrou est déjà pris ou non, la surcharge est d'environ 2 instructions atomiques OU deux exécutions du planificateur et le temps de sommeil - qui sera généralement beaucoup plus que 200x.
Klaas van Gend

6

Les limites de atomicsont importantes. Ils doivent être détaillés sur les spécifications OpenMP . MSDN propose une feuille de triche rapide car je ne serais pas surpris si cela ne changera pas. (Visual Studio 2012 a une implémentation OpenMP de mars 2002.) Pour citer MSDN:

L'instruction d'expression doit avoir l'une des formes suivantes:

xbinop =expr

x++

++x

x--

--x

Dans les expressions précédentes: xest une lvalueexpression de type scalaire. exprest une expression de type scalaire et ne fait pas référence à l'objet désigné par x. binop est pas un opérateur surchargé et est l' un +, *, -, /, &, ^, |, <<, ou >>.

Je recommande d'utiliser atomicquand vous le pouvez et de nommer les sections critiques autrement. Les nommer est important; vous éviterez ainsi de déboguer les maux de tête.


1
Ce ne sont pas tout, nous avons d'autres directives atomiques avancées comme: #pragma omp aromic update (ou read, upate, write, capture) donc cela nous permet d'avoir une autre déclaration bénéfique
pooria

1

Déjà de bonnes explications ici. Cependant, nous pouvons plonger un peu plus profondément. Pour comprendre la différence fondamentale entre les concepts de section atomique et critique dans OpenMP, nous devons d'abord comprendre le concept de verrouillage . Voyons pourquoi nous devons utiliser des verrous .

Un programme parallèle est exécuté par plusieurs threads. Des résultats déterministes se produiront si et seulement si nous effectuons une synchronisation entre ces threads. Bien entendu, la synchronisation entre les threads n'est pas toujours requise. Nous nous référons aux cas où la synchronisation est nécessaire.

Afin de synchroniser les threads dans un programme multi-thread, nous utiliserons lock . Lorsque l'accès doit être limité par un seul thread à la fois, les verrous entrent en jeu. L' implémentation du concept de verrouillage peut varier d'un processeur à l'autre. Voyons comment un simple verrou peut fonctionner d'un point de vue algorithmique.

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'algorithme donné peut être implémenté dans le langage matériel comme suit. Nous supposerons un seul processeur et analyserons le comportement des verrous. Pour cette pratique, supposons l'un des processeurs suivants: MIPS , Alpha , ARM ou Power .

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

Ce programme semble être OK, mais ce n'est pas le cas. Le code ci-dessus souffre du problème précédent; synchronisation . Trouvons le problème. Supposons que la valeur initiale de lock soit égale à zéro. Si deux threads exécutent ce code, l'un peut atteindre le SW R1, verrouiller avant que l'autre ne lise la variable de verrouillage . Ainsi, tous deux pensent que la serrure est libre. Pour résoudre ce problème, une autre instruction est fournie plutôt que de simples LW et SW . Il s'agit d'une instruction Read-Modify-Write . Il s'agit d'une instruction complexe (composée de sous-instructions) qui garantit que la procédure d' acquisition de verrouillage est effectuée par un seulfil à la fois. La différence de lecture-modification-écriture par rapport aux instructions simples de lecture et d' écriture est qu'il utilise une méthode différente de chargement et de stockage . Il utilise LL (Load Linked) pour charger la variable de verrouillage et SC (Store Conditional) pour écrire dans la variable de verrouillage. Un registre de lien supplémentaire est utilisé pour garantir que la procédure d'acquisition de verrouillage est effectuée par un seul thread. L'algorithme est donné ci-dessous.

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

Lorsque le registre de lien est réinitialisé, si un autre thread a supposé que le verrou est libre, il ne pourra plus écrire la valeur incrémentée dans le verrou. Ainsi, la concurrence d'accès à la variable de verrouillage est acquise.

La principale différence entre critique et atomique vient de l'idée que:

Pourquoi utiliser des verrous (une nouvelle variable) alors que nous pouvons utiliser la variable réelle (sur laquelle nous effectuons une opération), comme variable de verrouillage?

L'utilisation d'une nouvelle variable pour les verrous conduira à une section critique , tout en utilisant la directive réelle variable comme verrou conduira au concept atomique . La section critique est utile lorsque nous effectuons beaucoup de calculs (plus d'une ligne) sur la variable réelle. En effet, si le résultat de ces calculs ne parvient pas à être écrit sur la variable réelle, toute la procédure doit être répétée pour calculer les résultats. Cela peut entraîner des performances médiocres par rapport à l'attente de la libération du verrou avant d'entrer dans une région hautement informatique. Ainsi, il est recommandé d'utiliser le atomique lorsqu'une région plus complexe en calcul est effectuée par la section intensive. directive chaque fois que vous souhaitez effectuer un seul calcul (x ++, x--, ++ x, --x, etc.) et d'utiliser critique


-5

atomic est relativement efficace lorsque vous devez activer l'exclusion mutuelle pour une seule instruction, ce qui n'est pas le cas pour omp critical.


13
Ce n'est rien de plus qu'une reformulation mal formulée de la réponse acceptée sans explication.
High Performance Mark

-5

atomic est une seule instruction Section critique, c'est-à-dire que vous vous verrouillez pour une seule exécution d'instruction

la section critique est un verrou sur un bloc de code

Un bon compilateur traduira votre deuxième code de la même manière qu'il fait le premier


C'est tout simplement faux. Ne parlez pas de choses que vous ne comprenez pas.
jcsahnwaldt Réintègre Monica
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.