Introducción al mundo de GPGPU

Esta es una pequeña introducción general al área de GPGPU. En este artículo explico un poco de qué se trata esta relativamente nueva área de conocimiento, y está apuntado a aquellas personas con nociones de programación interesadas en utilizar tarjetas gráficas para resolver problemas de computación.

En la industria de los fabricantes de hardware gráfico ha estado surgiendo una utilidad que no se había previsto cuando comenzó el boom de acelerar los gráficos mediante estos dispositivos hace ya varios años. Todo comienza con el uso de las tarjetas gráficas para hacer otras cosas distintas a… dibujar algo en la pantalla para el usuario. De unos años para acá el mundo académico se ha vuelto loco con las capacidades de GPGPU, que son las siglas de General Processing for Graphics Processor Units.

Nvidia GeForce 6 6600GT card
Una tarjeta Nvidia GeForce 6600GT. Un modelo más de tarjeta gráfica. [Fuente]

GPGPU engloba todas las técnicas en las que se utiliza el poder de las tarjetas gráficas para hacer procesamiento general. Desde tareas consideradas relativamente mundanas, como buscar valores u ordenar arreglos, hasta hacer cálculos físicos como el estado de un líquido o el movimiento de una tela. Su capacidad para resolver esta clase de problemas que es el queso en la tostada que ven los académicos, y que han comenzado a aprovechar los desarrolladores.

¿Por qué preferrir las tarjetas de video a los procesadores?

La mayoría de los problemas físicos de los que hablamos en este artículo requieren de procesar una gran cantidad de datos en un tiempo muy corto. Un ejemplo de esto es que los líquidos suelen definirse como la interacción de muchas partículas, por lo que se tiene que procesar la información de cada partícula para calcular un estado. La naturaleza de estos datos, entonces, no es secuencial: en vez de ir punto por punto procesando información, es necesario procesarlos de manera paralela.

Fluid Simulation example
Un ejemplo de simulación de fluidos, utilizando un GPU para calcular su estado en el tiempo. Screenshot de Plasma Pong, un juego de pong con un agregado de fluidos. [Fuente]

Es ésta la diferencia fundamental entre un procesador como lo conocemos hasta ahora (llamémoslo CPU) y los procesadores de las tarjetas gráficas (llamémoslos GPUs). Los CPUs fabricados por Intel o AMD son unidades que procesan un dato a la vez y lo devuelven, es decir, son procesadores de una instrucción y un dato. A esto se le conoce como procesador SISD, siglas en inglés de Single Instruction, Single Data. A diferencia de éstos, los GPUs son capaces de aplicar una sola instrucción a muchos datos en el mismo tiempo, es decir, son procesadores de una instrucción y múltiples datos, procesadores conocidos como SIMD, siglas en inglés de Single Instruction, Multiple Data.

Cuando se utiliza un GPU para resolver un problema físico, la diferencia en rapidez con su contraparte hecha en CPU es por lo general de orden exponencial. Los problemas físicos al ser resueltos con una solución paralelizable, se pueden ejecutar en un GPU con una rapidez que no puede ser alcanzada por un CPU normal y corriente.

GPU chip GeForce FX 5600
Un procesador en una tarjeta gráfica puede aplicar una sola instrucción a muchos más datos que un CPU. Éste es un procesador de una tarjeta Nvidia Geforce FX 5600 [Fuente]

¿Cómo lo hacen?

Mientras que un CPU contiene una unidad encargada de procesar un dato a la vez, una tarjeta gráfica contiene varias unidades de procesamiento, a las que se les asigna una instrucción, y luego se les pone a procesar los datos.

Esto último se entiende mejor si explicamos por qué las tarjetas gráficas funcionan así. Las tarjetas gráficas trabajan fundamentalmente con información gráfica, un tipo de información que suele necesitar una gran cantidad de información procesada en un tiempo corto. Esta información gráfica, por lo general imágenes, se traducen en bits de información a los que se les suele aplicar la misma instrucción. Si suponemos 1 byte para representar el color de un pixel en una imagen de 1024×768, una imagen que ya puede ser considerada pequeña para los estándares que manejamos hoy en día, tendríamos que aplicar una instrucción para 786.432 bytes. Si encima agregamos que debemos procesar esta imagen 60 veces por segundo (para ofrecer al usuario una buena experiencia gráfica), multiplicando el anterior número por 60 estamos viendo que es necesario procesar una cantidad cercana a 42MB de información cada segundo.

Luego, las tarjetas gráficas incluyen procesadores, llamados fragment shaders, que vienen instalados de 4 a 8 en una tarjeta. Siendo hardware especializado, tienen un rendimiento superior haciendo una clase de tareas en específico que un CPU. Lo descrito anteriormente es una exagerada simplificación de cómo es la arquitectura

Pasemos entonces a programarlo todo en GPU

Antes de comenzar a pasar todos los algoritmos a GPU, hay que entender la forma de programar este tipo de soluciones. Es necesario entender que la forma de programar cuando se utiliza un GPU es totalmente distinta a cuando se utiliza un CPU. Esto se debe a las restricciones que impone el hardware gráfico para alcanzar esta rapidez. Este hardware todavía se utiliza fundamentalmente para dibujar gráficos, por lo que aún se maneja una terminología propia de esta área. El gran obstáculo que tiene GPGPU a la hora de resolver un problema es cómo utilizar esta maquinaria, especializada en dibujar gráficos, para hacer cosas más generales.

Así que tenemos que una tarjeta gráfica recibe información como vértices de un objeto, los colores de estos vértices, la textura que van a utilizar, las coordenadas de la textura que tienen. La tarjeta gráfica se encarga de transformar esta información en pixeles (aunque el término más preciso para esto son fragmentos), que luego son dibujados en la memoria que representa la pantalla, un área llamada el framebuffer. Como notarán, los términos que utilizamos acá son totalmente distintos a los que utilizamos cuando queremos hacer algo en un CPU.

Sin embargo, es posible hacer una traducción entre CPU y GPU. Esto es lo que ha permitido que GPGPU como área de estudio se haya desarrollado. Así que en algunos casos es factible representar los datos que queremos procesar como una textura, donde cada pixel es parte del estado. El objetivo sería aplicar una instrucción, o una serie de instrucciones, a cada pixel de esta textura, y dibujar el resultado en el framebuffer. El paso final sería leer los resultados del framebuffer y ponerlos en la memoria principal de la computadora para su posterior interpretación.

En principio, para hacer procesamiento general se limitaba a aplicar las funciones de la tarjeta gráfica que estaban presentes en el lenguaje de la aplicación. De un momento para acá se ha desarrollado un procesador de pixeles más genérico, al que se le pueden dar una serie de instrucciones para que lo aplique con cada fragmento. Cuando salieron esta clase de procesadores, el único lenguaje disponible era una versión de assembler específico para ellos.

Hoy en día existen lenguajes de mayor nivel, que permiten expresar estas instrucciones más como lo propone el problema que cómo una adaptación a lenguaje de máquina. La corporación Nvidia desde hace algún tiempo ha propuesto el lenguaje Cg (C for graphics), mientras que Microsoft propone HLSL (High-Level Shading Language), desarrollado en conjunto con nVidia, y utilizado en su librería DirectX. Por otra parte, la junta de OpenGL, uno de los grandes estándares de librerías gráficas, propone el uso de GLSL (GL Shading Language).

¿Hacia dónde va GPGPU?

Hemos visto que el procedimiento para hacer procesamiento general en una tarjeta gráfica no es más que una gran vuelta que el programador tiene que hacer para lograr su objetivo. La realidad es que el programador tiene que hacer muchas cosas en las que puede equivocarse, dando como resultado que hacer GPGPU sea un proceso largo y propenso a errores.

Las empresas de hardware han visto el potencial que tiene esta área y está comenzando a lanzar más productos apuntados a hacer GPGPU de manera más sencilla. Nvidia ha sido una de las empresas líderes en este campo, y comenzó lanzando un lenguaje específico para GPGPU, llamado CUDA, junto con dos librerías de cálculo, uno para álgebra lineal (BLAS), y otro para transformaciones rápidas de Fourier (FFT). Nvidia ha lanzado también procesadores específicos para GPGPU, con la línea Tesla de computadoras especializadas en procesamiento general. De seguir en esta tendencia, quizás veamos pronto un nicho sólido del hardware gráfico.

¿De aquí a dónde?

GPGPU es un tema grande, y aquí lo cubrimos de una manera muy general. Un punto de partida ideal para aprender más sobre este tema sería GPGPU.org, que incluye láminas y tutoriales para saber más de este tema. Para resolver problemas más específico sería necesario referirse a las páginas de aquellas personas que estén trabajando en esos problemas.

El libro The Cg Tutorial escrito por Mark J. Kilgard, experto en esta área, y publicado por la Addison-Wesley, lo recomiendo personalmente con los ojos cerrados para aprender este lenguaje y lanzarse de lleno a aprender a programar con GPUs.

Deja un comentario