cuda, sincronización de bloque ficticia/implícita

Soy consciente de que la sincronización de bloques no es posible, la única forma es lanzar un nuevo kernel.

PERO, supongamos que lanzo X bloques, donde X corresponde al número de SM en mi GPU. Debo considerar que el planificador asignará un bloque a cada SM... ¿verdad? Y si la GPU se está utilizando como una tarjeta gráfica secundaria (completamente dedicada a CUDA), esto significa que, en teoría, ningún otro proceso la usa... ¿no?

Mi idea es la siguiente: sincronización implícita.

Supongamos que a veces necesito solo un bloque y, a veces, necesito todos los bloques X. Bueno, en aquellos casos en los que necesito solo un bloque, puedo configurar mi código para que el primer bloque (o el primer SM) funcione en los datos "reales" mientras que los otros bloques X-1 (o SM) en algunos " datos ficticios", ejecutando exactamente la misma instrucción, solo que con algún otro desplazamiento.

Para que todos sigan sincronizados, hasta que los vuelva a necesitar a todos.

¿Es fiable el programador en estas condiciones? ¿O nunca puedes estar seguro?

preguntado el 04 de julio de 12 a las 08:07

2 Respuestas

Tienes varias preguntas en una, así que intentaré abordarlas por separado.

Un bloque por SM

Pregunté esto hace un tiempo Foros propios de nVidia, ya que estaba obteniendo resultados que indicaban que esto no es lo que sucede. Aparentemente, el planificador de bloques no asignará un bloque por SM si el número de bloques es igual al número de SM.

Sincronización implícita

No. En primer lugar, no puede garantizar que cada bloque tenga su propio SM (ver arriba). En segundo lugar, todos los bloques no pueden acceder a la tienda global al mismo tiempo. Si se ejecutan sincrónicamente, perderán esta sincronicidad a partir de la primera lectura/escritura de memoria.

Sincronización de bloques

Ahora las buenas noticias: sí, puedes. Las instrucciones atómicas descritas en la Sección B.11 del Guía de programación CUDA C se puede utilizar para crear una barrera. Suponga que tiene N bloques que se ejecutan simultáneamente en su GPU.

__device__ int barrier = N;

__global__ void mykernel ( ) {

    /* Do whatever it is that this block does. */
    ...

    /* Make sure all threads in this block are actually here. */
    __syncthreads();

    /* Once we're done, decrease the value of the barrier. */
    if ( threadIdx.x == 0 )
        atomicSub( &barrier , 1 );

    /* Now wait for the barrier to be zero. */
    if ( threadIdx.x == 0 )
        while ( atomicCAS( &barrier , 0 , 0 ) != 0 );

    /* Make sure everybody has waited for the barrier. */
    __syncthreads();

    /* Carry on with whatever else you wanted to do. */
    ...

    }

La instrucción atomicSub(p,i) calcula *p -= i atómicamente y solo es llamado por el hilo cero en el bloque, es decir, solo queremos decrementar barrier una vez. La instrucción atomicCAS(p,c,v) conjuntos *p = v si *p == c y devuelve el valor antiguo de *p. Esta parte simplemente se repite hasta que barrier alcances 0, es decir, hasta que todos los bloques lo hayan cruzado.

Tenga en cuenta que debe envolver esta parte en llamadas a __synchtreads() ya que los subprocesos en un bloque no se ejecutan en estricto paso de bloqueo y debe obligarlos a todos a esperar el subproceso cero.

Solo recuerda que si llamas a tu kernel más de una vez, debes establecer barrier de nuevo a N.

Noticias

En respuesta a jHackTheRipperla respuesta y cigarra¡Debería haber señalado que no debería intentar iniciar más bloques de los que se pueden programar simultáneamente en la GPU! Esto está limitado por una serie de factores, y debe utilizar el Calculadora de ocupación CUDA para encontrar el número máximo de bloques para su núcleo y dispositivo.

Sin embargo, a juzgar por la pregunta original, solo se inician tantos bloques como SM, por lo que este punto es discutible.

contestado el 23 de mayo de 17 a las 13:05

@elect: Sí, en realidad uso esto en mi propio código, aunque sin las llamadas a __syncthread() ya que solo tengo 32 hilos por bloque. Si duda en confiar en mi palabra, puede consultar el Apéndice A de "CUDA por ejemplo: una introducción a la programación de GPU de uso general", en el que se analizan las operaciones atómicas, la exclusión mutua y la sincronización entre bloques. - Pedro

-1 ¡Lo siento, pero esto está mal! Consulte la respuesta de jHackTheRipper para obtener una explicación. - user703016

@djmj Lo sé, pero estoy ejecutando un algoritmo que tiene que ejecutar miles de ciclos. En cada uno de estos ciclos necesito diferentes grados de paralelización, es decir a veces necesito solo un bloque de 34 hilos ya veces N bloques (siempre de 34 t) con N [1,34]. El punto es que cada llamada del kernel como una sobrecarga entre 3-20 µs en sistemas NO WDDM (donde dicen que es mucho más alta). Y ahora mismo estoy en win7 ^^. Sin embargo, espero cambiar lo antes posible a Linux para tener gastos generales más bajos. En cualquier caso, sería bueno evitarlos por completo, ¡quizás con solo una llamada al kernel! :pags - elegir

@djmj Solo quiero actualizar sobre los gastos generales del kernel en los sistemas WDDM. Dicen que no menos de 40 µs (comparado con los 3 µs). Es probable que sea más alto. - elegir

Lo que sugieres es posible, pero peligroso. Ver stackoverflow.com/questions/7703443/… - harrismo

¡@Pedro definitivamente está equivocado!

Lograr la sincronización global ha sido objeto de varios trabajos de investigación recientemente y, por último, para arquitecturas no Kepler (todavía no tengo ninguna). La conclusión es siempre la misma (o debería ser): no es posible lograr una sincronización tan global en toda la GPU.

La razón es simple: los bloques CUDA no se pueden adelantar, por lo que, dado que ocupa completamente la GPU, los subprocesos que esperan el encuentro de la barrera nunca permitirán que el bloque termine. Por lo tanto, no se eliminará del SM y evitará que se ejecuten los bloques restantes.

Como consecuencia, simplemente congelará la GPU que nunca podrá escapar de este estado de punto muerto.

-- editar para responder a los comentarios de Pedro --

Tales deficiencias han sido advertidas por otros autores como: http://www.openclblog.com/2011/04/eureka.html

por el autor de OpenCL en acción

-- editar para responder al segundo comentario de Pedro --

@Jared Hoberock llega a la misma conclusión en esta publicación de SO: Barrera entre bloques en CUDA

contestado el 23 de mayo de 17 a las 11:05

No, no estoy "definitivamente equivocado", de lo contrario esto no funcionaría en mi propio código. He agregado un comentario sobre la cantidad máxima de bloques, que aborda sus inquietudes sobre los interbloqueos. En cuanto a los "varios trabajos de investigación" que dicen que esto no es posible, ¿podría señalarme uno o dos de ellos? - Pedro

esto no es una cuestión de bloques programados simultáneamente, sino de ejecución simultánea: jopaserat

¿Cómo se define la ejecución simultánea? Puede programe hasta ocho bloques por SM que serán todos corrida intermitentemente Mientras un bloque está girando en el while-loop, los otros bloques en el mismo SM aún pueden ejecutarse, llenando las ranuras entre cada acceso a la memoria. Por cierto, sigo esperando los "varios trabajos de investigación". - Pedro

Una nota en la parte superior de una publicación de blog sobre OpenCL, es decir no CUDA. Eso no es exactamente "varios trabajos de investigación". Intente más, o al menos publique un contraejemplo en el que esto falle. Si va a decir que las respuestas de otras personas son "definitivamente incorrectas", tendrá que trabajar un poco más. - Pedro

Después de más de un año en Cuda, estoy llegando al punto en que la teoría importa solo hasta cierto punto. También pensé que la sincronización de bloques no era posible después de buscar mucho en Google, pero si Pedro dice que le está funcionando, no veo por qué debería mentir. No me malinterpreten, muchachos, no estoy diciendo que uno de ustedes tenga razón al 100%, solo digo que voy a intentarlo (tan pronto como encuentre algo de tiempo para implementarlo: D). Estoy seguro de que hay tantos factores (tanto hw como sw) en el juego y deberíamos averiguar cuál de ellos importa. En cualquier caso, ¡los mantendré informados a ambos! :) elegir - elegir

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.