He estado usando CUDA durante algunas semanas, pero tengo algunas dudas sobre la asignación de bloques / warps / thread. Estoy estudiando la arquitectura desde un punto de vista didáctico (proyecto universitario), por lo que alcanzar mi máximo rendimiento no es mi preocupación.
En primer lugar, me gustaría entender si entendí bien estos hechos:
El programador escribe un núcleo y organiza su ejecución en una cuadrícula de bloques de hilos.
Cada bloque se asigna a un Multiprocesador de transmisión (SM). Una vez asignado, no puede migrar a otro SM.
Cada SM divide sus propios bloques en Warps (actualmente con un tamaño máximo de 32 hilos). Todos los hilos de una urdimbre se ejecutan simultáneamente en los recursos del SM.
La ejecución real de un hilo es realizada por los núcleos CUDA contenidos en el SM. No existe un mapeo específico entre hilos y núcleos.
Si un warp contiene 20 hilos, pero actualmente solo hay 16 núcleos disponibles, el warp no se ejecutará.
Por otro lado, si un bloque contiene 48 hilos, se dividirá en 2 urdimbres y se ejecutarán en paralelo siempre que haya suficiente memoria disponible.
Si un subproceso se inicia en un núcleo, se detiene para el acceso a la memoria o para una operación de coma flotante larga, su ejecución podría reanudarse en un núcleo diferente.
¿Son correctas?
Ahora, tengo una GeForce 560 Ti, de acuerdo con las especificaciones, está equipada con 8 SM, cada una con 48 núcleos CUDA (384 núcleos en total).
Mi objetivo es asegurarme de que cada núcleo de la arquitectura ejecute las MISMAS instrucciones. Suponiendo que mi código no requerirá más registros que los disponibles en cada SM, imaginé diferentes enfoques:
Creo 8 bloques de 48 hilos cada uno, para que cada SM tenga 1 bloque para ejecutar. En este caso, ¿se ejecutarán los 48 hilos en paralelo en el SM (explotando todos los 48 núcleos disponibles para ellos)?
¿Hay alguna diferencia si ejecuto 64 bloques de 6 hilos? (Suponiendo que se asignarán de manera uniforme entre los SM)
Si "sumerjo" la GPU en el trabajo programado (creando 1024 bloques de 1024 hilos cada uno, por ejemplo), es razonable suponer que todos los núcleos se usarán en un cierto punto y realizarán los mismos cálculos (suponiendo que los hilos nunca pararse)?
¿Hay alguna forma de verificar estas situaciones con el generador de perfiles?
¿Hay alguna referencia para estas cosas? Leí la guía de programación CUDA y los capítulos dedicados a la arquitectura de hardware en "Programación de procesadores paralelos masivos" y "Diseño y desarrollo de aplicaciones CUDA"; pero no pude obtener una respuesta precisa.
fuente
Respuestas:
Dos de las mejores referencias son
Trataré de responder cada una de tus preguntas.
El programador divide el trabajo en hilos, hilos en bloques de hilos y bloques de hilos en cuadrículas. El distribuidor de trabajo de cálculo asigna bloques de subprocesos a multiprocesadores de transmisión (SM). Una vez que se distribuye un bloque de subprocesos a un SM, los recursos para el bloque de subprocesos se asignan (deformaciones y memoria compartida) y los subprocesos se dividen en grupos de 32 subprocesos llamados deformaciones. Una vez que se asigna una deformación, se denomina deformación activa. Los dos programadores warp seleccionan dos warps activos por ciclo y envían warps a las unidades de ejecución. Para obtener más detalles sobre las unidades de ejecución y el envío de instrucciones, consulte 1 p.7-10 y 2 .
4 ' . Hay un mapeo entre laneid (índice de hilos en una urdimbre) y un núcleo.
5 ' . Si una urdimbre contiene menos de 32 hilos, en la mayoría de los casos se ejecutará igual que si tuviera 32 hilos. Las deformaciones pueden tener menos de 32 subprocesos activos por varias razones: el número de subprocesos por bloque no es divisible por 32, el programa ejecuta un bloque divergente para que los subprocesos que no tomaron la ruta actual se marquen como inactivos o salga un subproceso en la distorsión.
6 ' . Un bloque de subprocesos se dividirá en WarpsPerBlock = (ThreadsPerBlock + WarpSize - 1) / WarpSize No hay ningún requisito para que los programadores de warp seleccionen dos warps del mismo bloque de subprocesos.
7 ' . Una unidad de ejecución no se detendrá en una operación de memoria. Si un recurso no está disponible cuando una instrucción está lista para ser enviada, la instrucción se enviará nuevamente en el futuro cuando el recurso esté disponible. Las deformaciones pueden detenerse en barreras, operaciones de memoria, operaciones de textura, dependencias de datos, ... Una deformación estancada no es elegible para ser seleccionada por el programador de deformaciones. En Fermi es útil tener al menos 2 warps elegibles por ciclo para que el programador warp pueda emitir una instrucción.
Consulte la referencia 2 para conocer las diferencias entre un GTX480 y GTX560.
Si lees el material de referencia (unos minutos) creo que encontrarás que tu objetivo no tiene sentido. Intentaré responder a tus puntos.
1 ' . Si inicia el núcleo <<< 8, 48 >>> obtendrá 8 bloques cada uno con 2 urdimbres de 32 y 16 hilos. No hay garantía de que estos 8 bloques se asignen a diferentes SM. Si se asignan 2 bloques a un SM, entonces es posible que cada planificador de warp pueda seleccionar un warp y ejecutar el warp. Solo usará 32 de los 48 núcleos.
2 ' . Hay una gran diferencia entre 8 bloques de 48 hilos y 64 bloques de 6 hilos. Supongamos que su núcleo no tiene divergencia y que cada hilo ejecuta 10 instrucciones.
Para obtener una eficiencia óptima, la división del trabajo debe estar en múltiplos de 32 hilos. El hardware no fusionará hilos de diferentes urdimbres.
3 ' . Un GTX560 puede tener 8 SM * 8 bloques = 64 bloques a la vez u 8 SM * 48 warps = 512 warps si el núcleo no maximiza los registros o la memoria compartida. En cualquier momento dado, una parte del trabajo estará activa en SM. Cada SM tiene múltiples unidades de ejecución (más que núcleos CUDA). Los recursos que se usan en un momento dado dependen de los programadores warp y la combinación de instrucciones de la aplicación. Si no realiza operaciones TEX, las unidades TEX estarán inactivas. Si no realiza una operación especial de coma flotante, las unidades SUFU estarán inactivas.
4 ' . Parallel Nsight y el programa Visual Profiler
a. IPC ejecutado
si. IPC emitido
C. deformaciones activas por ciclo activo
re. deformaciones elegibles por ciclo activo (solo Nsight)
mi. razones de pérdida de deformación (solo Nsight)
F. hilos activos por instrucción ejecutada
El generador de perfiles no muestra el porcentaje de utilización de ninguna de las unidades de ejecución. Para GTX560 se emitiría una estimación aproximada IPC / MaxIPC. Para MaxIPC, suponga que GF100 (GTX480) es 2 GF10x (GTX560) es 4 pero el objetivo es 3 es un mejor objetivo.
fuente
"E. Si un warp contiene 20 hilos, pero actualmente solo hay 16 núcleos disponibles, el warp no se ejecutará".
Es incorrecto. Está confundiendo los núcleos en su sentido habitual (también se usa en CPU): la cantidad de "multiprocesadores" en una GPU, con núcleos en el lenguaje de marketing nVIDIA ("nuestra tarjeta tiene miles de núcleos CUDA").
Un warp en sí solo puede programarse en un solo núcleo (= multiprocesador) y puede ejecutar hasta 32 hilos al mismo tiempo; No puede usar más de un núcleo.
El número "48 urdimbres" es el número máximo de urdimbres activos (urdimbres que pueden elegirse para ser programados para el trabajo en el próximo ciclo, en cualquier ciclo dado) por multiprocesador, en GPU nVIDIA con capacidad de cálculo 2.x; y este número corresponde a 1536 = 48 x 32 hilos.
Respuesta basada en este seminario web
fuente