¿Cuál es la diferencia entre atómico y crítico en OpenMP?

111

¿Cuál es la diferencia entre atómico y crítico en OpenMP?

puedo hacer esto

#pragma omp atomic
g_qCount++;

pero no es esto lo mismo que

#pragma omp critical
g_qCount++;

?

codereviewanskquestions
fuente

Respuestas:

173

El efecto en g_qCount es el mismo, pero lo que se hace es diferente.

Una sección crítica de OpenMP es completamente general: puede rodear cualquier bloque de código arbitrario. Sin embargo, usted paga por esa generalidad al incurrir en gastos generales significativos cada vez que un hilo entra y sale de la sección crítica (además del costo inherente de serialización).

(Además, en OpenMP todas las secciones críticas sin nombre se consideran idénticas (si lo prefiere, solo hay un bloqueo para todas las secciones críticas sin nombre), de modo que si un hilo está en una sección crítica [sin nombre] como se indicó anteriormente, ningún hilo puede ingresar a ninguna sección crítica [sin nombre]. Como puede adivinar, puede evitar esto utilizando secciones críticas con nombre).

Una operación atómica tiene una sobrecarga mucho menor. Cuando está disponible, se aprovecha de que el hardware proporciona (digamos) una operación de incremento atómico; en ese caso, no es necesario bloquear / desbloquear al ingresar / salir de la línea de código, solo hace el incremento atómico que el hardware le dice que no puede ser interferido.

Las ventajas son que la sobrecarga es mucho menor, y que un hilo en una operación atómica no bloquea ninguna operación atómica (diferente) que esté a punto de suceder. La desventaja es el conjunto restringido de operaciones que soporta atomic.

Por supuesto, en cualquier caso, incurrirá en el costo de la serialización.

Jonathan Dursi
fuente
5
"podría perder portabilidad" - No estoy seguro de que esto sea cierto. El estándar (versión 2.0) especifica qué operaciones atómicas están permitidas (básicamente cosas como ++y *=) y que si no son compatibles con el hardware, pueden ser reemplazadas por criticalsecciones.
Dan R
@DanRoche: Sí, tienes razón. No creo que esa afirmación fuera correcta alguna vez, la corregiré ahora.
Jonathan Dursi
Hace unos días seguí un tutorial de OpenMP y, según tengo entendido, hay una diferencia en los dos códigos diferentes. Ese es el resultado puede diferir porque la sección crítica asegura que la instrucción es ejecutada por un hilo una vez, sin embargo es posible que la instrucción: g_qCount = g_qCount + 1; para el hilo 1 simplemente almacena el resultado g_qCount solo en el búfer de escritura, no en la memoria RAM, y cuando el hilo 2 obtiene el valor g_qCount, simplemente lee el que está en la RAM, no en el búfer de escritura. La instrucción atómica asegura que la instrucción descargó los datos en la memoria
Giox79
30

En OpenMP, todas las secciones críticas sin nombre son mutuamente excluyentes.

La diferencia más importante entre crítica y atómica es que atómica puede proteger solo una única asignación y puede usarla con operadores específicos.

Miguel
fuente
13
Mejor hubiera sido un comentario (o una edición) de la respuesta anterior.
kynan
20

Sección crítica:

  • Asegura la serialización de bloques de código.
  • Puede ampliarse para serializar grupos de bloques con el uso adecuado de la etiqueta "nombre".

  • ¡Más lento!

Operación atómica:

  • ¡Es mucho más rápido!

  • Solo asegura la serialización de una operación en particular.

efarsarakis
fuente
9
Pero esta respuesta es muy legible y sería un gran resumen de la primera respuesta
Michał Miszczyszyn
7

La forma más rápida no es ni crítica ni atómica. Aproximadamente, la adición con sección crítica es 200 veces más cara que la simple adición, la adición atómica es 25 veces más cara que la simple adición.

La opción más rápida (no siempre aplicable) es darle a cada hilo su propio contador y hacer una operación de reducción cuando necesite la suma total.

Andrii
fuente
2
No estoy de acuerdo con todos los números que menciona en su explicación. Suponiendo x86_64, la operación atómica tendrá algunos ciclos de sobrecarga (sincronizando una línea de caché) en el costo de aproximadamente un ciclo. Si, de lo contrario, tendría un costo de "participación real", los gastos generales son nihil. Una sección crítica incurre en el costo de una cerradura. Dependiendo de si el bloqueo ya está activado o no, la sobrecarga son aproximadamente 2 instrucciones atómicas O dos ejecuciones del programador y el tiempo de suspensión, que generalmente será significativamente más de 200x.
Klaas van Gend
6

Las limitaciones de atomicson importantes. Deben detallarse en las especificaciones de OpenMP . MSDN ofrece una hoja de trucos rápida, ya que no me sorprendería que esto no cambie. (Visual Studio 2012 tiene una implementación de OpenMP desde marzo de 2002). Para citar MSDN:

La declaración de expresión debe tener una de las siguientes formas:

xbinop =expr

x++

++x

x--

--x

En las expresiones anteriores: xes una lvalueexpresión con tipo escalar. expres una expresión con tipo escalar y no hace referencia al objeto designado por x. binop no es un operador sobrecargado y es uno de +, *, -, /, &, ^, |, <<, o >>.

Recomiendo usarlo atomiccuando pueda y nombrar secciones críticas de lo contrario. Nombrarlos es importante; evitará depurar los dolores de cabeza de esta manera.

darda
fuente
1
Esto no es todo, tenemos otras directivas atómicas avanzadas como: #pragma omp actualización aromic (o leer, actualizar, escribir, capturar) por lo que nos permite tener alguna otra declaración beneficiosa
pooria
1

Ya hay grandes explicaciones aquí. Sin embargo, podemos sumergirnos un poco más. Para comprender la diferencia central entre los conceptos de sección atómica y crítica en OpenMP, primero debemos comprender el concepto de bloqueo . Repasemos por qué necesitamos usar candados .

Varios subprocesos están ejecutando un programa paralelo. Los resultados deterministas ocurrirán si y solo si realizamos la sincronización entre estos hilos. Por supuesto, la sincronización entre subprocesos no siempre es necesaria. Nos referimos a aquellos casos en los que la sincronización es necesaria.

Para sincronizar los subprocesos en un programa de subprocesos múltiples, usaremos lock . Cuando se requiere que el acceso esté restringido por un solo hilo a la vez, los bloqueos entran en juego. La implementación del concepto de bloqueo puede variar de un procesador a otro. Averigüemos cómo puede funcionar un candado simple desde un punto de vista algorítmico.

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

El algoritmo dado se puede implementar en el lenguaje de hardware de la siguiente manera. Asumiremos un solo procesador y analizaremos el comportamiento de los bloqueos en ese. Para esta práctica, supongamos uno de los siguientes procesadores: MIPS , Alpha , ARM o Power .

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

Este programa parece estar bien, pero no lo es. El código anterior adolece del problema anterior; sincronización . Busquemos el problema. Suponga que el valor inicial de bloqueo es cero. Si dos subprocesos ejecutan este código, uno podría llegar al SW R1, bloquear antes de que el otro lea la variable de bloqueo . Así, ambos piensan que la cerradura es gratis. Para resolver este problema, se proporciona otra instrucción en lugar de LW y SW simples . Se llama instrucción de lectura-modificación-escritura . Es una instrucción compleja (que consta de subinstrucciones) que asegura el hilo de adquisición de bloqueo procedimiento de lo realiza un solo a la vez. La diferencia de lectura-modificación-escrituraen comparación con las sencillas instrucciones de lectura y escritura , es que utiliza una forma diferente de carga y almacenamiento . Utiliza LL (Load Linked) para cargar la variable de bloqueo y SC (Store Conditional) para escribir en la variable de bloqueo. Se utiliza un registro de enlace adicional para garantizar que el procedimiento de adquisición de bloqueo se realice mediante un solo hilo. El algoritmo se da a continuación.

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

Cuando se restablece el registro de enlace, si otro subproceso ha asumido que el bloqueo está libre, no podrá volver a escribir el valor incrementado en el bloqueo. Por lo tanto, la concurrencia de acceso a la cerradura se adquiere variable de .

La diferencia central entre crítica y atómica proviene de la idea de que:

¿Por qué usar bloqueos (una nueva variable) mientras podemos usar la variable real (en la que estamos realizando una operación), como una variable de bloqueo?

El uso de una nueva variable para cerraduras conducirá a la sección crítica , mientras que el uso de la variable real como cerradura conducirá al concepto atómico . La sección crítica es útil cuando estamos realizando muchos cálculos (más de una línea) en la variable real. Esto se debe a que, si el resultado de esos cálculos no se escribe en la variable real, se debe repetir todo el procedimiento para calcular los resultados. Esto puede conducir a un rendimiento deficiente en comparación con esperar a que se libere el bloqueo antes de ingresar a una región altamente computacional. Por lo tanto, se recomienda usar la directiva atómica siempre que desee realizar un solo cálculo (x ++, x--, ++ x, --x, etc.) y usardirectiva crítica cuando la sección intensiva está realizando una región más compleja computacionalmente.

hexfeo
fuente
-5

atomic es relativamente eficiente en el rendimiento cuando necesita habilitar la exclusión mutua para una sola instrucción, algo similar no ocurre con omp crítico.

Mahesh
fuente
13
Esto no es más que una reformulación mal redactada de la respuesta aceptada sin explicación.
Marca de alto rendimiento
-5

atomic es una sección crítica de una sola instrucción, es decir, se bloquea para la ejecución de una instrucción

la sección crítica es un bloqueo en un bloque de código

Un buen compilador traducirá su segundo código de la misma manera que lo hace con el primero.

Wissam Y. Khalil
fuente
Eso está mal. Por favor, no hables de cosas que no entiendas.
jcsahnwaldt reinstala a Monica