openmp - introduccion - pragma omp sections
¿Cuál es la diferencia entre atómica y crítica en OpenMP? (8)
¿Cuál es la diferencia entre atómica y crítica en OpenMP?
puedo hacer esto
#pragma omp atomic
g_qCount++;
pero no es lo mismo
#pragma omp critical
g_qCount++;
?
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 esa generalidad al incurrir en una sobrecarga significativa cada vez que un hilo entra y sale de la sección crítica (además del costo inherente de la 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, ningún hilo puede ingresar sección crítica [sin nombre]. Como usted puede adivinar, puede evitar esto usando secciones críticas con nombre).
Una operación atómica tiene una sobrecarga mucho menor. Donde esté disponible, se aprovecha el hardware que proporciona (digamos) una operación de incremento atómico; en ese caso no es necesario el bloqueo / desbloqueo al entrar / salir de la línea de código, solo hace el incremento atómico que el hardware dice que no se puede interferir.
Las ventajas son que la sobrecarga es mucho menor, y un hilo que se encuentra en una operación atómica no bloquea ninguna (diferente) operación atómica a punto de suceder. La desventaja es el conjunto restringido de operaciones que admite atómica.
Por supuesto, en cualquier caso, incurre en el costo de la serialización.
En OpenMP, todas las secciones críticas sin nombre son mutuamente excluyentes.
La diferencia más importante entre crítico y atómico es que atomic puede proteger solo una asignación y se puede usar con operadores específicos.
La manera 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 costosa que la simple adición, la adición atómica es 25 veces más costosa que la simple adición.
La opción más rápida (no siempre aplicable) es dar a cada hilo su propio contador y reducir la operación cuando se necesita suma total.
Las limitaciones de atomic
son importantes. Deben detallarse en las especificaciones de OpenMP . MSDN ofrece una hoja de referencia rápida, ya que no me sorprendería que esto no cambie. (Visual Studio 2012 tiene una implementación OpenMP desde marzo de 2002). Para citar a MSDN:
La declaración de expresión debe tener una de las siguientes formas:
x
binop =expr
x++
++x
x--
--x
En las expresiones anteriores:
x
es una expresiónlvalue
con tipo escalar.expr
es una expresión con tipo escalar, y no hace referencia al objeto designado porx
. binop no es un operador sobrecargado y es uno de+
,*
,-
,/
,&
,^
,|
,<<
, o>>
.
Recomiendo usar atomic
cuando sea posible y nombrar secciones críticas de lo contrario. Nombrarlos es importante; usted evitará dolores de cabeza de depuración de esta manera.
Ya hay grandes explicaciones aquí. Sin embargo, podemos sumergirnos un poco más profundo. Para comprender la diferencia central entre los conceptos de sección crítica y atómica en OpenMP, primero tenemos que entender el concepto de bloqueo . Repasemos por qué necesitamos usar bloqueos .
Un programa paralelo está siendo ejecutado por múltiples hilos. Los resultados deterministas ocurrirán si y solo si realizamos la sincronización entre estos hilos. Por supuesto, la sincronización entre hilos no siempre es necesaria. Nos referimos a aquellos casos en que la sincronización es necesaria.
Para sincronizar los hilos en un programa multiproceso, utilizaremos el bloqueo . Cuando se requiere que el acceso esté restringido por solo un hilo a la vez, los bloqueos entran en juego. La implementación del concepto de bloqueo puede variar de un procesador a otro. Veamos cómo un bloqueo simple puede funcionar 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 eso. 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 sufre del problema anterior; sincronización Encontremos el problema. Supongamos que el valor inicial del bloqueo es cero. Si dos hilos ejecutan este código, uno puede alcanzar el SW R1, bloquear antes de que el otro lea la variable de bloqueo . Por lo tanto, ambos piensan que el bloqueo es gratis. Para resolver este problema, hay otra instrucción proporcionada en lugar de simple LW y SW . Se llama instrucción Read-Modify-Write . Es una instrucción compleja (que consta de subinstrucciones) que asegura que el procedimiento de adquisición de bloqueo se realiza con un solo hilo a la vez. La diferencia de Read-Modify-Write en comparación con las sencillas instrucciones de lectura y escritura es que utiliza una forma diferente de cargar y almacenar . 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 único 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 hilo ha asumido que el bloqueo está libre, no podrá volver a escribir el valor incrementado en el bloqueo. Por lo tanto, se adquiere la simultaneidad de acceso a la variable de bloqueo .
La diferencia central entre crítica y atómica proviene de la idea de que:
¿Por qué utilizar bloqueos (una nueva variable) mientras que podemos usar la variable real (que estamos realizando una operación sobre ella) como una variable de bloqueo?
Usar una nueva variable para los bloqueos conducirá a la sección crítica , mientras que usar la variable real como un bloqueo conducirá al concepto atómico . La sección crítica es útil cuando estamos realizando una gran cantidad de 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 pobre 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 utilizar la directiva atómica cada vez que desee realizar un solo cálculo (x ++, x--, ++ x, --x, etc.) y usar directivas críticas cuando una región más compleja computacionalmente esté siendo realizada por la sección intensiva
atomic es relativamente eficiente en cuanto a rendimiento cuando necesita habilitar la exclusión mutua para que solo una instrucción similar no sea cierta acerca de omp critical.
atomic es una declaración única Sección crítica, es decir, se bloquea para una ejecución de sentencia
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
Sección crítica:
- Garantiza la serialización de bloques de código.
Se puede ampliar para serializar grupos de bloques con el uso adecuado de la etiqueta "nombre".
¡Más lento!
Operación atómica:
Es mucho mas rapido!
Solo asegura la serialización de una operación en particular.