- La GPU es un procesador masivamente paralelo con miles de núcleos, diseñado para manejar muchas tareas al mismo tiempo
- La CPU destaca en tareas complejas y secuenciales, pero la cantidad de trabajos que puede procesar a la vez es limitada
- En cambio, la GPU procesa tareas en paralelo mediante miles de hilos, por lo que sobresale al manejar grandes volúmenes de datos con rapidez
- Por ejemplo, la GPU NVIDIA RTX 4090 tiene 16,384 núcleos CUDA, en comparación con los 16 a 24 núcleos de una CPU avanzada
- Cada núcleo de GPU es más lento que un núcleo de CPU, pero el procesamiento paralelo de una enorme cantidad de núcleos la hace adecuada para cálculos a gran escala como las operaciones de matrices
CUDA y Python
- CUDA (Compute Unified Device Architecture) de NVIDIA es una plataforma y una extensión de C++ que permite escribir programas que se ejecutan en la GPU
- CUDA proporciona un modelo de programación y APIs para que los desarrolladores puedan escribir código que se ejecute directamente en la GPU
- Esto permite descargar de la CPU a la GPU las tareas que pueden procesarse en paralelo para mejorar el rendimiento
- Los desarrolladores de Python pueden aprovechar la aceleración por GPU con herramientas como Numba
- Numba es un compilador de Python que compila código de Python para ejecutarlo en GPUs compatibles con CUDA
- Gracias a esto, los desarrolladores de Python pueden empezar fácilmente con la computación acelerada por GPU con una cantidad mínima de sintaxis y términos nuevos
- CUDA Python ofrece wrappers de Cython/Python para las APIs del controlador y del runtime de CUDA, permitiendo a los desarrolladores de Python aprovechar la computación paralela de la GPU
- CUDA Python puede instalarse mediante PIP y Conda
Estructura de hilos y bloques en CUDA
- En CUDA, un kernel es una función que se ejecuta en la GPU; al lanzar un kernel, cientos o miles de hilos paralelos se ejecutan al mismo tiempo y procesan datos distintos
- Este modelo se conoce como modelo SIMT (Single-Instruction Multiple-Thread)
- Los hilos se organizan en warps (grupos de 32 hilos), y los warps se agrupan en bloques
- Cada bloque se ejecuta en un streaming multiprocessor (SM), y cada SM tiene recursos limitados, como registros y memoria compartida
- El tamaño del bloque influye en la asignación de esos recursos y en la cantidad de warps que pueden ejecutarse simultáneamente (occupancy)
- Al ajustar adecuadamente la cantidad y el tamaño de los bloques de hilos, es posible aprovechar con eficiencia los recursos de la GPU
Gestión de memoria y optimización
- En la programación CUDA, la gestión de memoria entre la CPU (host) y la GPU (device) debe realizarse de forma explícita
- El flujo general es el siguiente:
- Asignar memoria en la GPU (
cudaMalloc)
- Copiar datos del host al device (
cudaMemcpy)
- Ejecutar el kernel
- Copiar los resultados del device al host (
cudaMemcpy)
- Liberar memoria de la GPU (
cudaFree)
- La memoria compartida es memoria on-chip que permite a los hilos dentro de un bloque compartir datos rápidamente, lo que mejora la velocidad de acceso a memoria
- La sincronización entre hilos se implementa con
__syncthreads(), lo que permite evitar race conditions
Kernels CUDA personalizados para LLM
- En las cargas de trabajo de modelos de lenguaje grandes (LLM), se están desarrollando kernels CUDA personalizados que combinan varias operaciones en un solo kernel para reducir la sobrecarga de memoria y aumentar la eficiencia
- Por ejemplo, FlashAttention optimiza el self-attention de Transformer reduciendo las lecturas y escrituras de memoria, lo que mejora significativamente la eficiencia
- FlashAttention aprovecha la memoria compartida para dividir el cómputo en tiles, logrando así una alta eficiencia incluso con secuencias largas
- Estas optimizaciones pueden resolver el problema de que el ancho de banda de memoria se convierta en un cuello de botella en deep learning
Comparación entre implementaciones de PyTorch y CUDA
- En PyTorch, las operaciones en GPU pueden realizarse fácilmente gracias a sus abstracciones de alto nivel
- Por ejemplo, una operación para sumar dos vectores puede implementarse de manera tan simple como esta:
import torch
# Crear dos vectores grandes en la GPU
a = torch.rand(1000000, device='cuda')
b = torch.rand(1000000, device='cuda')
# Suma elemento por elemento
c = a + b
- Sin embargo, cuando se necesita optimizar el rendimiento, es posible escribir kernels personalizados usando CUDA directamente
- Con CUDA, se pueden ajustar con precisión los patrones de acceso a memoria, la configuración de hilos y el uso de memoria compartida para maximizar el rendimiento
- Por ejemplo, la implementación en CUDA de FlashAttention optimiza el acceso a memoria y divide las operaciones en tiles en memoria compartida para mejorar el rendimiento
- Mediante este tipo de optimizaciones de bajo nivel, se puede lograr un rendimiento superior al de la implementación de alto nivel de PyTorch
Conclusión
- Al aprovechar la capacidad de procesamiento paralelo de la GPU, es posible realizar con eficiencia el procesamiento de grandes volúmenes de datos y cálculos complejos
- CUDA es una plataforma que permite aprovechar al máximo ese rendimiento de la GPU, y los desarrolladores de Python también pueden beneficiarse de CUDA mediante herramientas como Numba
- Comprender la estructura de hilos y bloques de CUDA, así como las técnicas de gestión de memoria, permite hacer programación GPU más eficiente
- En particular, en áreas como el deep learning, se puede maximizar el rendimiento escribiendo kernels CUDA personalizados
- Incluso usando frameworks de alto nivel como PyTorch, también es posible buscar un mayor rendimiento mediante optimizaciones CUDA de bajo nivel cuando sea necesario
1 comentarios
Comentarios de Hacker News
Pregunta tonta: como ingeniero, ¿es posible profundizar bastante en el nivel bajo de CUDA o de la arquitectura de GPU sin aprender el lado matemático de la IA? Si sí, ¿cómo se podría empezar? Siento que tendría que aprender optimización y por qué se usan las GPU para ciertos cálculos
Muy buen artículo. Los cuestionarios en línea (QnA), que parecen generados por IA, son muy útiles para poner a prueba la comprensión. Ojalá todos los tutoriales incluyeran esta función
Me pregunto si todos los tutoriales de CUDA están orientados a IA, o si también los hay, por ejemplo, para cómputo científico general. Suena divertido intentar cosas como flujo de aire sobre un ala para computación de alto rendimiento
Gracias por compartirlo, disfruté leerlo. Tengo una pregunta algo relacionada: me pregunto si hay alguna idea de cómo DeepSeek logró evitar CUDA para hacer la ejecución más eficiente
Lo que Jensen da, Guido lo quita
Este libro: "Programming Massively Parallel Processors" parece hecho a la medida para quienes pasan de la arquitectura de CPU a la de GPU
También recomendaría revisar https://github.com/rust-gpu/rust-gpu y https://github.com/rust-gpu/rust-cuda
Enlaces relacionados: https://sakana.ai/ai-cuda-engineer/ y https://reddit.com/r/MachineLearning/…
Me pregunto si alguien tiene alguna idea de qué cambió recientemente y permitió hacer simulaciones de punta a punta en GPU (por ejemplo, isaac sim) que antes solo eran posibles en CPU
Como esto está en el sitio web de PySpur, me pregunto si alguien tiene experiencia con PySpur y herramientas de UI para agentes de IA como n8n. Estoy buscando algo que me ayude a prototipar algunas ideas por diversión. Como tendría que hacer self-hosting ($), preferiría algo relativamente fácil de configurar, como Open Hands