Cómo son los hilos que se organizaron para ser ejecutado por una GPU?

  • La Programación CUDA Guía debe ser un buen lugar para comenzar para esto. También me gustaría recomendar el CUDA introducción de aquí.

2 Comentarios

  1. 275

    De Hardware

    Si una GPU dispositivo tiene, por ejemplo, 4 multiprocesamiento unidades, y se puede ejecutar 768 hilos de cada uno de ellos: a continuación, en un momento dado no más de 4*768 hilos será realmente se ejecuta en paralelo (si planificado más de los hilos, se espera su turno).

    Software

    hilos están organizados en bloques. Un bloque es ejecutado por un multiprocesamiento unidad.
    Los hilos de un bloque puede ser identificado (indexado) utilizando 1 la medida(x), 2Dimensions (x,y) o 3Dim índices (x,y,z), pero en cualquier caso xyz <= 768 para nuestro ejemplo (se aplican otras restricciones a x,y,z, consulte la guía y la capacidad del dispositivo).

    Obviamente, si usted necesita más que esos 4*768 hilos necesita más de 4 bloques.
    Los bloques también pueden ser indexados 1D, 2D o 3D. Hay una cola de bloques de espera para entrar
    la GPU (porque, en nuestro ejemplo, la GPU tiene 4 multiprocesadores y a sólo 4 cuadras son
    se ejecuta de forma simultánea).

    Ahora un caso sencillo: el procesamiento de una imagen de 512×512

    Supongamos que queremos un hilo de proceso de un solo píxel (i,j).

    Podemos utilizar bloques de 64 hilos cada uno. Entonces tenemos 512*512/64 = 4096 bloques
    (así que para tener 512×512 hilos = 4096*64)

    Es común para organizar (para hacer la indexación de la imagen más fácil) de los hilos en 2D bloques de blockDim = 8 x 8 (64 hilos por bloque). Yo prefiero llamarlo threadsPerBlock.

    dim3 threadsPerBlock(8, 8);  //64 threads

    y 2D gridDim = 64 x 64 bloques (la 4096 bloques necesarios). Yo prefiero llamarlo numBlocks.

    dim3 numBlocks(imageWidth/threadsPerBlock.x,  /* for instance 512/8 = 64*/
                  imageHeight/threadsPerBlock.y); 

    El kernel es lanzado como este:

    myKernel <<<numBlocks,threadsPerBlock>>>( /* params for the kernel function */ );       

    Por último: no va a ser algo así como «una cola de 4096 bloques», donde un bloque está a la espera de ser asignado a uno de los multiprocesadores de la GPU para obtener sus 64 subprocesos ejecutados.

    En el núcleo del pixel (i,j) para ser procesados por un hilo se calcula de esta manera:

    uint i = (blockIdx.x * blockDim.x) + threadIdx.x;
    uint j = (blockIdx.y * blockDim.y) + threadIdx.y;
    • Si cada bloque puede ejecutar 768 hilos, ¿por qué usar sólo el 64? Si utiliza el límite máximo de 768, usted tendrá menos bloques y así un mejor rendimiento.
    • los bloques son lógico, el límite de 768 hilos es para cada uno de los física unidad de procesamiento. Utiliza bloques, de acuerdo a las especificaciones de su problema con el fin de distribuir el trabajo a los hilos. No se cree probable que usted siempre puede utilizar los bloques de 768 hilos para cada problema que usted tiene. Imagine que usted tiene que procesar una imagen de 64 x 64 (4096 píxeles). 4096/768 = 5.333333 bloques ?
    • el bloque lógico, pero cada bloque se asigna a un núcleo. si hay más bloques de núcleo, los bloques están en la cola hasta que los núcleos de ser libre. En su ejemplo, usted puede utilizar a 6 cuadras y tiene el extra hilos no hacer nada(2/3 de los hilos en el 6º bloque).
    • No es trivial para ajustar la configuración de cuda para obtener el máximo provecho de ella. Además, recuerde que los algoritmos complejos pueden ser más claras por escrito en términos de bloques con dimensiones tales que a x b x c < 768. Si no era tan simple de usar siempre 768 hilos x bloque, a continuación, el cuda diseñadores no habría de hacer posible que cualquier otra organización de los hilos.
    • Los bloques más el más lento, el programa es? También me gustaría comprobar que antes de decir eso en voz alta para un determinado problema. Hay muchas otras cosas que afecta el rendimiento de un núcleo: lo de la memoria que utiliza (global, las texturas, los almacena y cómo se accede a ella (coalescencia). Consulte las mejores prácticas para la guía de programación de cuda.
    • Por último, hay un límite en la cantidad de memoria disponible para los hilos de cada bloque. Si cada subproceso utiliza demasiado «local» variables de entonces, el número de hilos en el bloque (blockDim) debe reducirse.
    • Creo Aliza el punto es buena: si es posible, uno quiere usar la mayor cantidad de hilos por bloque como sea posible. Si hay una restricción que requiere un menor número de hilos, mejor para que explique por qué podría ser el caso en un segundo ejemplo (pero todavía explicar el más simple y más conveniente caso, en primer lugar).
    • Sí, tal vez. Pero el caso es que la cantidad de memoria requerida por cada hilo es dependiente de la aplicación. Por ejemplo, en mi último programa, cada hilo invoca un menos-plaza de la optimización de la función, que requiere «mucho» de la memoria. Por lo tanto, que los bloques no puede ser más grande de 4×4 hilos. Aún así, el speedup obtenido fue espectacular, frente a la versión secuencial.
    • se puede explicar de qué hacer cuando el tamaño de la imagen no es divisible por 64. Para el tamaño de la imagen 511×511/64 = 4080.01
    • Debe rellenar la imagen, la adición de píxeles (o recortar, quitar píxeles) para que se adapte a una potencia de 2 dimensión.
    • Buena respuesta. Pero, al empezar a hablar de «multiprocesamiento unidades», que me dice nada. GPU constan de SM, el último consisten en Núcleos y aquellos que consisten en hilos. Que parte es que «el multiprocesamiento de la unidad»?

  2. 6

    supongamos que una 9800GT GPU:
    14 multiprocesadores, cada uno tiene 8 threadprocessors y warpsize es de 32 lo que significa que cada threadprocessor maneja hasta 32 hilos.
    14*8*32=3584 es el número máximo de realidad cuncurrent hilos.

    si se ejecuta este kernel con más de 3584 hilos (es decir 4000 hilos, y no importa cómo se defina el bloque y la cuadrícula. la gpu va a tratar como de la misma):

    func1();
    __syncthreads();
    func2();
    __syncthreads();

    a continuación, el orden de ejecución de estas dos funciones son como sigue:

    1.func1 se ejecuta por primera 3584 hilos

    2.func2 se ejecuta por primera 3584 hilos

    3.func1 es ejecutado por el resto de hilos

    4.func2 es ejecutado por el resto de hilos

    • ¿Qué sucede si func2() depende de los resultados de func1(). Creo que esto está mal
    • Esto lo escribí hace siete años, pero si recuerdo correctamente hice una prueba en esto y me esta conclusión de que los núcleos con más hilos de la gpu se comportan de esta manera. Si le sucede a prueba este caso y llegó a un resultado diferente, a continuación, voy a tener que borrar este post.

Dejar respuesta

Please enter your comment!
Please enter your name here