Quelle est la différence entre atomique et critique en 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++;
?
Quelle est la différence entre atomique et critique en 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++;
?
L'effet sur g_qCount est le même, mais ce qui est fait est différent.
Une section critique 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 subissant une surcharge significative à chaque fois qu'un thread entre et sort de la section critique (en plus du coût inhérent à la sérialisation).
(En outre, 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 une section critique [sans nom]. Comme vous pouvez le deviner, vous pouvez contourner ce problème en utilisant des sections critiques nommées).
Une opération atomique a un surcoût beaucoup plus faible. Lorsqu'elle est disponible, elle tire avantage du matériel qui fournit (par exemple) une opération d'incrémentation atomique ; dans ce cas, il n'est pas nécessaire de verrouiller/déverrouiller l'entrée/sortie de la ligne de code, il suffit d'effectuer l'incrémentation atomique qui, selon le matériel, ne peut pas être interférée.
Les avantages sont que l'overhead est beaucoup plus faible et qu'un thread se trouvant dans une opération atomique ne bloque pas d'autres opérations atomiques (différentes) sur le point de se produire. L'inconvénient est l'ensemble restreint d'opérations que l'atomique supporte.
Bien sûr, dans les deux cas, vous devez supporter le coût de la sérialisation.
"vous pourriez perdre la portabilité" - Je ne suis pas sûr que ce soit vrai. Le site norme (version 2.0) spécifie quelles opérations atomiques sont autorisées (en gros, des choses comme ++
y *=
) et que s'ils ne sont pas pris en charge par le matériel, ils pourraient être remplacés par des critical
sections.
@DanRoche : Oui, vous avez tout à fait raison. Je ne pense pas que cette déclaration ait jamais été correcte, je vais la corriger maintenant.
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 dans les deux codes différents. C'est à dire que 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 seulement dans le writebuffer pas dans la mémoire RAM, et quand le thread 2 récupère la valeur g_qCount, il lit simplement celle dans la RAM, pas dans le writebuffer. L'instruction atomique assure que l'instruction a effacé les données en mémoire.
Le chemin le plus rapide n'est ni critique ni atomique. Approximativement, l'addition avec section critique est 200 fois plus chère que l'addition simple, l'addition atomique est 25 fois plus chère que l'addition simple.
L'option la plus rapide (pas toujours applicable) est de donner à chaque fil son propre compteur et de faire une opération de réduction lorsque vous avez besoin de la somme totale.
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 un surcoût 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, l'overhead est nul. Une section critique engendre le coût d'un verrou. Selon que le verrou est déjà pris ou non, le surcoût est d'environ 2 instructions atomiques OU deux exécutions du planificateur et le temps de sommeil - ce qui sera généralement beaucoup plus que 200x.
L'option que vous suggérez pourrait entraîner une demande énorme de mémoire que nous n'avons peut-être pas à notre disposition. Par exemple, si je travaille sur des données de 1000x1000x1000 cellules et que je travaille avec 10 ou 100 threads, les copies internes créées pour chaque thread vont certainement saturer la RAM.
Les limites de la atomic
sont importantes. Ils doivent être détaillés sur le Spécifications de l'OpenMP . MSDN propose un aide-mémoire rapide car je ne serais pas surpris que cela ne change pas. (Visual Studio 2012 possède une implémentation d'OpenMP datant de mars 2002.) Pour citer MSDN :
La déclaration d'expression doit avoir l'une des formes suivantes :
x
binop \=expr
x++
++x
x--
--x
Dans les expressions précédentes :
x
est unlvalue
expression de type scalaire.expr
est une expression de type scalaire, et elle ne fait pas référence à l'objet désigné parx
. binop n'est pas un opérateur surchargé et est l'un des éléments suivants+
,*
,-
,/
,&
,^
,|
,<<
o>>
.
Je recommande d'utiliser atomic
quand vous le pouvez et nommé des sections critiques sinon. Il est important de leur donner un nom ; vous éviterez ainsi les problèmes de débogage.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.