Un bloque de hilos es una abstracción de programación que representa un grupo de hilos que pueden ejecutarse en serie o en paralelo. Para una mejor asignación de procesos y datos , los hilos se agrupan en bloques de hilos. El número de hilos en un bloque de hilos estaba anteriormente limitado por la arquitectura a un total de 512 hilos por bloque, pero a partir de marzo de 2010, con la capacidad de cómputo 2.x y superior, los bloques pueden contener hasta 1024 hilos. Los hilos en el mismo bloque de hilos se ejecutan en el mismo multiprocesador de flujo. [ 1 ] Los hilos en el mismo bloque pueden comunicarse entre sí a través de memoria compartida , sincronización de barrera u otras primitivas de sincronización como operaciones atómicas.
Se combinan varios bloques para formar una cuadrícula. Todos los bloques de la misma cuadrícula contienen el mismo número de hilos. El número de hilos por bloque es limitado, pero las cuadrículas se pueden usar para cálculos que requieren que un gran número de bloques de hilos operen en paralelo y utilicen todos los multiprocesadores disponibles.
CUDA es una plataforma de computación paralela y un modelo de programación que los lenguajes de alto nivel pueden usar para aprovechar el paralelismo. En CUDA, el kernel se ejecuta mediante hilos. Un hilo es una entidad abstracta que representa la ejecución del kernel. Un kernel es una función que se compila para ejecutarse en un dispositivo específico. Las aplicaciones multihilo utilizan muchos de estos hilos que se ejecutan simultáneamente para organizar la computación paralela. Cada hilo tiene un índice, que se usa para calcular las direcciones de memoria y también para tomar decisiones de control.
Dimensiones
CUDA opera con un modelo de programación heterogéneo que se utiliza para ejecutar programas de aplicación en dispositivos anfitriones. Su modelo de ejecución es similar al de OpenCL . En este modelo, la aplicación se ejecuta en el dispositivo anfitrión, que suele ser un núcleo de CPU . Este dispositivo está orientado al rendimiento, es decir, cuenta con una GPU que realiza cálculos en paralelo. Para estas ejecuciones paralelas se utilizan funciones kernel. Una vez ejecutadas, el control se devuelve al dispositivo anfitrión, que reanuda la ejecución en serie.
Como muchas aplicaciones paralelas involucran datos multidimensionales, es conveniente organizar los bloques de hilos en matrices de hilos de 1D, 2D o 3D. Los bloques en una cuadrícula deben poder ejecutarse de forma independiente, ya que la comunicación o cooperación entre bloques en una cuadrícula no es posible. 'Cuando se lanza un kernel, se especifica el número de hilos por bloque de hilos y el número de bloques de hilos, lo que, a su vez, define el número total de hilos CUDA lanzados. [ 2 ] ' Las dimensiones máximas x, y y z de un bloque son 1024, 1024 y 64, y debe asignarse de manera que x × y × z ≤ 1024, que es el número máximo de hilos por bloque. [ 3 ] Los bloques pueden organizarse en cuadrículas de una, dos o tres dimensiones de hasta 2 31 -1, 65,535 y 65,535 bloques en las dimensiones x, y y z respectivamente. [ 3 ] A diferencia del número máximo de hilos por bloque, no existe un límite de bloques por cuadrícula distinto de las dimensiones máximas de la cuadrícula.
Indexación
Indexación 1D
En CUDA, cada hilo está asociado a un índice específico para poder calcular y acceder a las ubicaciones de memoria en un array.
Consideremos un ejemplo con una matriz de 512 elementos. Una de las estructuras organizativas consiste en una cuadrícula con un único bloque de 512 hilos. Supongamos que existe una matriz C de 512 elementos, formada por la multiplicación elemento a elemento de dos matrices A y B, ambas de 512 elementos cada una. Cada hilo tiene un índice i y realiza la multiplicación del i- ésimo elemento de A y B, almacenando el resultado en el i -ésimo elemento de C. El índice i se calcula utilizando blockIdx (que es 0 en este caso, ya que solo hay un bloque), blockDim (512 en este caso, dado que el bloque tiene 512 elementos) y threadIdx, que varía de 0 a 511 para cada bloque.

El índice del hilo i se calcula mediante la siguiente fórmula :
blockIdx.x es el identificador del bloque de la dimensión x.
blockDim.x es la dimensión x de la dimensión del bloque
threadIdx.x es la dimensión x del identificador del hilo.
Por lo tanto, 'i' tendrá valores que van desde 0 hasta 511, lo que cubre todo el array.
Si queremos realizar cálculos con un array de más de 1024 elementos, podemos usar varios bloques con 1024 hilos cada uno. Consideremos un ejemplo con un array de 2048 elementos. En este caso, tenemos dos bloques de hilos con 1024 hilos cada uno. Por lo tanto, los identificadores de los hilos variarán de 0 a 1023, el identificador del bloque variará de 0 a 1 y la dimensión del bloque será de 1024. Así, el primer bloque tendrá índices de 0 a 1023 y el último tendrá índices de 1024 a 2047.
Thus each thread will first calculate the index of memory that it has to access and then proceed with the calculation. Consider an example in which elements from arrays A and B are added in parallel by using threads and the results is stored in an array C. The corresponding code in a thread is shown below :[5]
__global__voidvecAddKernel(float*A,float*B,float*C,intn){intindex=blockIdx.x*blockDim.x+threadIdx.x;if(index<n){C[index]=A[index]+B[index];}}2D-indexing
In the same way in particularly complex grids, the blockId as well as the threadId need to be calculated by each thread depending on geometry of the grid. Consider, a 2-dimensional Grid with 2-dimensional blocks. The threadId and the blockId will be calculated by the following formulae :
Hardware perspective
Although we have stated the hierarchy of threads, we should note that, threads, thread blocks and grid are essentially a programmer's perspective. In order to get a complete gist of thread block, it is critical to know it from a hardware perspective. The hardware groups threads that execute the same instruction into warps. Several warps constitute a thread block. Several thread blocks are assigned to a Streaming Multiprocessor (SM). Several SM constitute the whole GPU unit (which executes the whole Kernel Grid).

Streaming multiprocessors
Each architecture in GPU (say Kepler or Fermi) consists of several SM or Streaming Multiprocessors. These are general purpose processors with a low clock rate target and a small cache. An SM is able to execute several thread blocks in parallel. As soon as one of its thread blocks has completed execution, it takes up the serially next thread block. In general, SMs support instruction-level parallelism but not branch prediction.[8]

Para lograr este propósito, un SM contiene lo siguiente: [ 8 ]
- Núcleos de ejecución. (unidades de punto flotante de precisión simple, unidades de punto flotante de doble precisión, unidades de funciones especiales (SFU)).
- Cachés:
- Caché L1 (para reducir la latencia de acceso a la memoria).
- Memoria compartida (para datos compartidos entre hilos).
- Caché constante (para la difusión de lecturas desde una memoria de solo lectura ).
- Caché de texturas (para agregar ancho de banda desde la memoria de texturas).
- Planificadores para warps. (Estos sirven para dar instrucciones a los warps según políticas de planificación específicas).
- Un número considerable de registros. (Un SM puede estar ejecutando una gran cantidad de hilos activos simultáneamente, por lo que es imprescindible contar con miles de registros).
El hardware programa los bloques de subprocesos en un SM. En general, un SM puede gestionar varios bloques de subprocesos simultáneamente. Un SM puede contener hasta 8 bloques de subprocesos en total. A cada subproceso se le asigna un ID mediante su SM correspondiente.
Cuando un SM ejecuta un bloque de hilos, todos los hilos que contiene se ejecutan simultáneamente. Por lo tanto, para liberar la memoria de un bloque de hilos dentro del SM, es fundamental que todos los hilos del bloque hayan finalizado su ejecución. Cada bloque de hilos se divide en unidades programadas conocidas como warp. Estas se describen en detalle en la siguiente sección.

El planificador de warps de SM decide cuál de los warps se prioriza durante la emisión de instrucciones. [ 11 ] Algunas de las políticas de priorización de warps también se han analizado en las siguientes secciones.
Distorsiones
En el lado del hardware, un bloque de hilos se compone de "warps". (Este término proviene de " weaving" [ 12 ] ) . Un warp es un conjunto de 32 hilos dentro de un bloque de hilos. En el pasado, se garantizaba que estos hilos se ejecutaran "en bloque" (todos los hilos dentro del warp ejecutaban una instrucción simultáneamente) y, fundamentalmente, que accedieran a cada posición de memoria con todos o ningún hilo del warp. Este comportamiento podía provocar fácilmente interbloqueos (por ejemplo, al usar bifurcaciones if en bucles). Sin embargo, desde la arquitectura Volta , es posible el intercambio de datos dentro del warp mediante bloqueos de grano más fino [ 13 ] [ 14 ] . Estos hilos son seleccionados en serie por el SM [ 15 ] .
Una vez que se inicia un bloque de hilos en un multiprocesador (SM), todos sus subprocesos (warps) permanecen en ejecución hasta que finaliza su procesamiento. Por lo tanto, no se inicia un nuevo bloque en un SM hasta que haya suficientes registros libres para todos los subprocesos del nuevo bloque y hasta que haya suficiente memoria compartida libre para dicho bloque.
Consideremos un warp de 32 hilos ejecutando una instrucción. Si uno o ambos operandos no están listos (por ejemplo, no se han obtenido aún de la memoria global), se produce un proceso llamado " cambio de contexto " que transfiere el control a otro warp. [ 16 ] Al cambiar de un warp en particular, todos los datos de ese warp permanecen en el archivo de registros para que pueda reanudarse rápidamente cuando sus operandos estén listos. Cuando una instrucción no tiene dependencias de datos pendientes, es decir, ambos operandos están listos, el warp correspondiente se considera listo para la ejecución. Si más de un warp es apto para la ejecución, el SM padre utiliza una política de planificación de warps para decidir qué warp obtiene la siguiente instrucción obtenida.
A continuación se analizan diferentes políticas para programar los warps que son elegibles para su ejecución: [ 17 ]
- Round Robin (RR): Las instrucciones se obtienen de forma rotativa. RR garantiza que los SM se mantengan ocupados y que no se desperdicien ciclos de reloj en latencias de memoria.
- Menos Recientemente Recuperado (LRF): en esta política, el warp para el que no se ha recuperado una instrucción durante más tiempo tiene prioridad en la recuperación de una instrucción.
- Fair (FAIR) [ 17 ] - En esta política, el planificador se asegura de que todos los warps tengan una oportunidad "justa" en cuanto al número de instrucciones que se les solicitan. Solicita instrucciones a un warp para el cual se ha solicitado el número mínimo de instrucciones.
- CAWS basado en bloques de hilos [ 18 ] (planificación de warps con conciencia de criticidad): el énfasis de esta política de planificación radica en mejorar el tiempo de ejecución de los bloques de hilos. Asigna más recursos de tiempo al warp que tardará más tiempo en ejecutarse. Al dar prioridad al warp más crítico, esta política permite que los bloques de hilos finalicen más rápido, de modo que los recursos estén disponibles con mayor rapidez.
El cambio de contexto de los hilos de la CPU tradicional requiere guardar y restaurar los valores de los registros asignados y el contador de programa en la memoria externa (o caché), por lo que es una operación mucho más compleja que el cambio de contexto de los warps. Todos los valores de los registros de un warp (incluido su contador de programa) permanecen en el archivo de registros, y la memoria compartida (y la caché) también se mantienen, ya que se comparten entre todos los warps del bloque de hilos.
Para aprovechar la arquitectura de subprocesos (warp), los lenguajes de programación y los desarrolladores deben comprender cómo agrupar los accesos a memoria y cómo gestionar la divergencia del flujo de control. Si cada subproceso en un subproceso sigue una ruta de ejecución diferente o si accede a memoria significativamente divergente, se pierden las ventajas de esta arquitectura y el rendimiento se degrada considerablemente.
Referencias
- ↑ "Capítulo 4. Implementación de hardware. Los hilos de un bloque de hilos se ejecutan simultáneamente en un multiprocesador, y varios bloques de hilos pueden ejecutarse simultáneamente en un multiprocesador" (PDF) .
- ↑ "Modelo de hilos CUDA" . www.olcf.ornl.gov . Archivado del original el 23 de septiembre de 2016. Consultado el 21 de septiembre de 2016 .
- 1 2 "Documentación del kit de herramientas CUDA: características y especificaciones técnicas" . docs.nvidia.com . Consultado el 24 de mayo de 2022 .
- ↑ "Jerarquía de hilos en la programación CUDA" . Consultado el 21 de septiembre de 2016 .
- ↑ Kirk, David; Hwu, Wen-mei W (28 de enero de 2010). Programación de procesadores masivamente paralelos: un enfoque práctico .
- ↑ "Hoja de referencia para la indexación de hilos" (PDF) . Consultado el 21 de septiembre de 2016 .
- ↑ "Optimización de hilos (Universidad de Mayland)" (PDF) .
- 1 2 Wilt, Nicholas (2013). El manual de CUDA: una guía completa para la programación de GPU .
- ↑ "Optimización de hilos (Universidad de Mayland)" (PDF) .
- ↑ "Optimización de hilos (Universidad de Mayland)" (PDF) .
- ↑ "Computación GPU con CUDA Lección 2 - Memorias CUDA" (PDF) .
- ↑ "Parallel Thread Execution ISA Versión 6.0" . Zona de desarrolladores: Documentación del kit de herramientas CUDA . NVIDIA Corporation. 22 de septiembre de 2017. Archivado del original el 28 de octubre de 2017. Consultado el 27 de octubre de 2017 .
- ↑ "1. Guía de ajuste de Volta — Documentación de la Guía de ajuste de Volta 13.0" . docs.nvidia.com . Consultado el 5 de agosto de 2025 .
- ↑ Nvidia. "Cuda C++ Programming Model V13" (PDF) . pág. 142. Consultado el 24 de agosto de 2025 .
- ↑ "Uso de primitivas de nivel de warp de CUDA" . Nvidia . 15 de enero de 2018. Consultado el 8 de abril de 2020.
Las GPU de NVIDIA ejecutan grupos de hilos conocidos como warps en el estilo SIMT (Single Instruction, Multiple Thread).
- ↑ "Problemas de memoria en CUDA y planificación de ejecución en CUDA" (PDF) .
- 1 2 "Efecto de la búsqueda de instrucciones y la planificación de memoria en el rendimiento de la GPU" (PDF) .
- ↑ "CAWS: Planificación de warps con conciencia de criticidad para cargas de trabajo GPGPU" (PDF) .
- Computación paralela