¿Qué es la programación de GPU?
En el mundo de la computación de alto rendimiento, las GPU (Graphics Processing Units) están en todas partes, y los científicos que trabajan en determinados campos se enfrentan a un desafío creciente. En concreto, necesitan ajustar sus programas informáticos para aprovechar al máximo estos sistemas avanzados basados en GPU. Esta tarea es fundamental para desbloquear todo el potencial de estas arquitecturas GPU de última generación.
En este artículo ofreceremos una comprensión básica de la programación en GPU para quienes buscan respuesta a la pregunta: “¿Qué es la programación en GPU?”. Además, explorarás distintos enfoques y marcos de trabajo para GPU, así como la plataforma más popular de programación en Python para GPU, CUDA, y su importancia dentro del desarrollo orientado a GPU.
¿Qué es una GPU?
Una unidad de procesamiento gráfico (GPU) es un circuito electrónico que acelera la creación de imágenes para su visualización mediante la manipulación rápida de memoria y el procesamiento simultáneo de datos. Hoy en día, las GPU son omnipresentes y se encuentran en numerosos dispositivos informáticos como ordenadores personales, consolas de videojuegos, estaciones de trabajo profesionales e incluso smartphones.
Con el tiempo, las GPU han superado a las CPU en ciertos aspectos de rendimiento, ya que integran más transistores y destacan en el cómputo paralelo. Al evolucionar hacia dispositivos complejos con su propia memoria, buses y procesadores, las GPU pueden considerarse como un cerebro adicional o un procesador complementario dentro del sistema informático.
¿Qué es la programación en GPU?
La programación en GPU utiliza aceleradores gráficos para realizar operaciones de computación de propósito general de forma simultánea. En el pasado, las GPU se usaban principalmente para gráficos por ordenador, pero actualmente también se emplean para todo tipo de tareas de computación general.
Además de generar gráficos, las GPU se utilizan ampliamente en modelado científico, aprendizaje automático y otros trabajos que pueden ejecutarse más rápido procesando múltiples tareas en paralelo.
¿Cuál es la diferencia entre GPU y CPU?
La principal diferencia entre la computación en GPU y CPU se encuentra en sus variaciones arquitectónicas. Debido a su naturaleza altamente paralela, una GPU es más eficaz que una CPU para manejar datos que pueden dividirse y procesarse de varias formas simultáneamente. Las GPU están específicamente optimizadas para cálculos intensivos como la aritmética matricial y las operaciones en coma flotante.
La diferencia de potencia de procesamiento entre CPU y GPU se debe a que las GPU están diseñadas para ejecutar tareas altamente paralelas y computacionalmente intensivas, exactamente lo que requiere el renderizado gráfico.
En la arquitectura de las GPU se da prioridad al procesamiento de datos frente al almacenamiento en caché y al control de flujo. Surgen dos ventajas importantes al abordar un problema en paralelo: en primer lugar, cada elemento se trata con el mismo problema, lo que reduce la necesidad de control de flujo complejo; en segundo lugar, un conjunto de datos grande y una alta intensidad aritmética disminuyen la necesidad de memoria de baja latencia.
¿Qué enfoques o APIs de programación GPU existen?
El uso de GPU para cálculos puede abordarse de distintas maneras. Ajustar parámetros y configuraciones iniciales puede ser suficiente en los casos más simples donde el código ya está escrito. Alternativamente, el uso de librerías puede resolver las partes más intensivas del código. Sin embargo, en escenarios más complejos puede ser necesario programar directamente.
Existen múltiples entornos de software y APIs para programación GPU, que pueden clasificarse en modelos basados en directivas, modelos basados en kernels no portables, modelos basados en kernels portables, así como frameworks y librerías de alto nivel.
C++/Fortran estándar
Los programas escritos en C++ o Fortran estándar pueden aprovechar las GPU de NVIDIA sin necesidad de librerías externas. Los compiladores del SDK de NVIDIA optimizan el código para su ejecución en GPU, tal como indican sus directrices oficiales.
Programación basada en directivas
Los enfoques basados en directivas consisten en anotar el código secuencial existente para indicar bucles y regiones que deben ejecutarse en la GPU. Este método, representado por OpenACC y OpenMP, está enfocado en la productividad y facilidad de uso, aunque puede sacrificar algo de rendimiento.
OpenACC: Consorcio formado en 2010 cuyo objetivo es proporcionar un modelo de programación estándar, portable y escalable para aceleradores, incluidas GPU. Inicialmente solo soportaba GPU NVIDIA, pero se está ampliando a más dispositivos y arquitecturas.
OpenMP: Originalmente era una API de programación paralela para CPU multinúcleo con memoria compartida, pero ahora también soporta offloading a GPU y puede trabajar con distintos tipos de GPU.
Modelos basados en kernels no portables (modelos nativos)
La programación directa en GPU ofrece a los desarrolladores mayor control mediante código de bajo nivel que se comunica directamente con la GPU. Ejemplos incluyen CUDA (NVIDIA) y HIP (AMD), siendo HIP un modelo diseñado para compatibilidad cruzada entre GPU NVIDIA y AMD.
Modelos basados en kernels portables (ecosistemas multiplataforma)
Los ecosistemas multiplataforma como Alpaka, Kokkos, OpenCL, RAJA y SYCL ofrecen abstracciones de mayor nivel que permiten una programación GPU más cómoda y portable. Su objetivo es la portabilidad de rendimiento con aplicaciones de fuente única.
Kokkos: Desarrollado en Sandia National Laboratories, es un ecosistema basado en C++ que permite aplicaciones paralelas eficientes y escalables en CPU, GPU y FPGA.
OpenCL: API estándar abierta para computación paralela de propósito general en distintos tipos de hardware. Proporciona una interfaz de bajo nivel para programación GPU y soporta CPU, GPU y FPGA de múltiples fabricantes.
SYCL: Modelo de programación en C++ abierto y libre de royalties que proporciona un modelo de fuente única de alto nivel para sistemas heterogéneos, incluidas GPU. Puede implementarse sobre OpenCL, CUDA o HIP.
Soporte de lenguajes de alto nivel
Python soporta programación en GPU mediante varias librerías, cada una con propósitos distintos:
CuPy
- Librería de arrays basada en GPU
- Integración directa con NumPy/SciPy
- Permite migrar fácilmente a GPU reemplazando 'numpy' y 'scipy' por 'cupy' y 'cupyx.scipy'
cuDF (RAPIDS)
- Parte del ecosistema RAPIDS, implementa funcionalidades CUDA con bindings en Python
- Diseñada para manipulación de dataframes en GPU
- Ofrece una API similar a pandas que permite acelerar tareas sin conocer profundamente CUDA
PyCUDA
- Entorno de programación Python para CUDA
- Permite acceder a la API CUDA de NVIDIA desde Python
- Limitado a GPU NVIDIA y requiere conocimientos de CUDA
Numba
- Permite compilación Just-In-Time del código Python para ejecución en GPU
- Soporta GPU NVIDIA y se prevé soporte para GPU AMD en el futuro
Estas librerías ofrecen múltiples opciones para programación GPU en Python, adaptándose a diferentes preferencias y necesidades. Tanto si se migra desde NumPy/SciPy con CuPy como si se utiliza la API tipo pandas de cuDF, Python proporciona flexibilidad para cálculos acelerados por GPU.
¿Qué es CUDA?
CUDA es una plataforma de computación paralela y una API desarrollada por NVIDIA que marcó el inicio de los frameworks de programación GPU de uso masivo. Esta tecnología permite a los desarrolladores escribir código similar a C++ que se ejecuta en la GPU.
CUDA no solo ofrece un amplio conjunto de librerías y herramientas para explorar la programación GPU a bajo nivel, sino que también mejora de forma significativa la eficiencia de aplicaciones computacionalmente exigentes. Cabe señalar que CUDA está limitado al hardware NVIDIA, incluso con un ecosistema tan potente.
Cómo instalar CUDA en Ubuntu 20.04 para programación GPU
Puedes instalar CUDA en Ubuntu 20.04 siguiendo estas instrucciones paso a paso:
Paso 1: Primero descarga el archivo de configuración Pin de CUDA (cuda-ubuntu2004.pin), necesario para configurar los repositorios CUDA en Ubuntu 20.04, usando ‘wget’ o ‘curl’.
$ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin
Paso 2: Mueve el archivo descargado al directorio correspondiente y establece su prioridad de pin en 600.
$ sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600
Paso 3: Descarga el paquete del repositorio CUDA:
wget https://developer.download.nvidia.com/compute/cuda/11.4.2/local_installers/cuda-repo-ubuntu2004-11-4-local_11.4.2-470.57.02-1_amd64.deb
Paso 4: Instala el paquete descargado usando el gestor de paquetes dpkg.
$ sudo dpkg -i cuda-repo-ubuntu2004-11-4-local_11.2-470.57.02-1_amd64.deb
Paso 5: Añade la clave pública necesaria para autenticar el repositorio CUDA.
$ sudo apt-key add /var/cuda-repo-ubuntu2004-11-4-local/7fa2af80.pub
Paso 6: Actualiza las listas de paquetes:
$ sudo apt-get update
Actualiza las listas locales con la información del repositorio CUDA recién añadido.
Paso 7: Ahora instala el toolkit CUDA en tu máquina Ubuntu 20.04 con el siguiente comando:
$ sudo apt-get -y install cuda
Siguiendo estos pasos, podrás instalar CUDA en una máquina equipada con una GPU compatible con CUDA y ejecutando Ubuntu 20.04. A partir de ese momento podrás ejecutar código GPU y utilizar la plataforma CUDA en tu sistema Linux.
Conclusión
La elección de un entorno de programación GPU depende de factores como las plataformas de hardware objetivo, la naturaleza de los cálculos y la experiencia del desarrollador. La compatibilidad con GPU específicas, la alineación con los requisitos del proyecto y las preferencias del desarrollador entre simplicidad de alto nivel o control de bajo nivel son consideraciones clave para tomar la decisión óptima.
Blog