2011-07-31 24 views
41

En OpenCL, tengo entendido que puede usar la función barrier() para sincronizar subprocesos en un grupo de trabajo. Sí (generalmente) entiendo para qué son y cuándo usarlos. También sé que todos los hilos en un grupo de trabajo deben golpear la barrera, de lo contrario hay problemas. Sin embargo, cada vez que he intentado utilizar barreras hasta ahora, parece que mi controlador de video se cuelga o aparece un mensaje de error sobre el acceso a algún tipo de memoria no válida. Hasta ahora he visto esto en 2 tarjetas de video diferentes (1 ATI, 1 NVIDIA).Barreras en OpenCL

Por lo tanto, mis preguntas son:

  1. Cualquier idea de por qué esto iba a pasar?
  2. ¿Cuál es la diferencia entre barrier(CLK_LOCAL_MEM_FENCE) y barrier(CLK_GLOBAL_MEM_FENCE)? Leí la documentación, pero no estaba claro para mí.
  3. ¿Existe una regla general sobre cuándo usar barrier(CLK_LOCAL_MEM_FENCE) frente a barrier(CLK_GLOBAL_MEM_FENCE)?
  4. ¿Hay alguna vez que llamar a barrier() con el tipo de parámetro incorrecto podría causar un error?

Respuesta

30

Como ha indicado, las barreras solo pueden sincronizar los hilos en el mismo grupo de trabajo. No hay forma de sincronizar diferentes grupos de trabajo en un kernel.

ahora para responder a su pregunta, la especificación no era claro para mí tampoco, pero me parece que la sección 6.11.9 contiene la respuesta:

CLK_LOCAL_MEM_FENCE - la función de barrera, o bien al ras ningún las variables almacenado en la memoria local o poner en cola una valla de memoria para asegurar ordenar correctamente las operaciones de memoria a la memoria local.

CLK_GLOBAL_MEM_FENCE - La función de barrera pondrá en cola una valla de memoria para asegurar el ordenamiento correcto de las operaciones de memoria en la memoria global. Esto puede ser útil cuando los elementos de trabajo, por ejemplo, escriben en el búfer o en los objetos de memoria de imagen y luego desean leer los datos actualizados.

Por lo tanto, a mi entender, se debe utilizar CLK_LOCAL_MEM_FENCE al escribir y leer al espacio __local memoria y CLK_GLOBAL_MEM_FENCE al escribir y readin al espacio __global memoria.

No he probado si esto es más lento, pero la mayoría de las veces, cuando necesito una barrera y tengo una duda sobre el que impacta el espacio de memoria, simplemente utilizan una combinación de los dos, es decir:

barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE); 

De esta forma no debería tener ningún problema de lectura/escritura de la escritura (siempre y cuando esté seguro de que cada hilo del grupo atraviesa la barrera, pero ya lo sabe).

Espero que ayude.

+3

Obviamente CLK_GLOBAL_MEM_FENCE es a menudo más lento que CLK_LOCAL_MEM_FENCE. El motivo es que todos los hilos dentro de un bloque tendrán que esperar a que finalicen los accesos a la memoria. Y esperar a que finalicen los accesos a la memoria global es mucho más costoso que los accesos a la memoria local. Por supuesto, esto no siempre es cierto, depende de los patrones de acceso (Fermi ahora tiene cachés, lo que significa que los accesos globales pueden almacenarse en caché en L1, que tiene latencia similar con memoria compartida), el número de accesos de memoria global/local en el kernel , la ocupación, conflictos bancarios, coaliciones, etc. – Zk1001

31

Reviviendo un hilo antiguo aquí. He tenido un poco de problemas con barrier().

En cuanto a su problema de colisión, una posible causa podría ser si su barrera está dentro de una condición.Leí que cuando usa la barrera, TODOS los elementos de trabajo en el grupo deben poder alcanzar esa instrucción, o colgará su kernel, lo que generalmente da como resultado un bloqueo.

if(someCondition){ 
    //do stuff 
    barrier(CLK_LOCAL_MEM_FENCE); 
    //more stuff 
}else{ 
    //other stuff 
} 

Mi opinión es que si uno o más elementos de trabajo satisface somecondition, todos los elementos de trabajo deben cumplir dicho requisito, o habrá algunos que saltar la barrera. Las barreras esperan hasta que TODOS los elementos de trabajo lleguen a ese punto. Para solucionar el código anterior, necesito reestructurar un poco:

if(someCondition){ 
    //do stuff 
} 
barrier(CLK_LOCAL_MEM_FENCE); 
if(someCondition){ 
    //more stuff 
}else{ 
    //other stuff 
} 

Ahora todos los elementos de trabajo se llega a la barrera.

No sé en qué medida esto se aplica a los bucles; si un elemento de trabajo se rompe de un bucle for, ¿toca barreras? No estoy seguro

ACTUALIZACIÓN: He bloqueado con éxito algunos programas ocl con una barrera en un bucle for. Asegúrese de que todos los elementos de trabajo salgan del bucle for al mismo tiempo, o mejor aún, coloque la barrera fuera del bucle.

(fuente: computación heterogénea con OpenCL Capítulo 5, p90-91)

+2

Gracias por su comentario. Tampoco estoy seguro, pero sospecho que todos los hilos en un grupo de trabajo deben alcanzar una barrera si lo hace algún hilo de ese grupo de trabajo. Entonces, el caso del bucle, si todos los hilos en un grupo de trabajo se rompen antes de tocar la barrera, no hay problema. Pero, si al menos un hilo de un grupo de trabajo golpea la barrera en una iteración particular del ciclo, entonces todos los hilos (de ese grupo de trabajo) también deben hacerlo. –

+0

Otro caso que la documentación parece omitir es también el caso de tener un if/else y llamar a una barrera en ambos casos. Esto no parece bloquear el programa, pero tampoco parece tener ningún efecto. He descubierto que el código de ejemplo de AMD mantiene todos los hilos en ejecución en bucles for, aunque algunos no tienen otro propósito que golpear la barrera. –