Me han empezado a salir canas. Cuando a los hombres nos pasa esto solemos apuntarnos a un gimnasio y comprarnos un deportivo.
Como soy un friqui y como no tengo pelas para un deportivo, este recordatorio de lo breve que es la vida me anima para que escriba de una vez una entrada en el blog sobre cómo funcionan las GPUs. Os lo tenía prometido y a este ritmo de envejecimiento en dos semanas estaré gagá y será demasiado tarde.
Creo que la mejor forma de explicar la arquitectura y el funcionamiento de una GPU es mediante comparación con los procesadores que todos conocemos, las CPUs.
Una CPU típica moderna tiene las siguientes características:
- Núcleos/"cores": 1~8 y aumentando.
- Unidades aritmético-lógicas: tanto escalares como SIMD (p. ej. instrucciones SSE).
- Frecuencia de reloj: 1~3 GHz.
- Hilos simultáneos por núcleo (hyperthreading): 1~2.
- Técnicas hardware para explotar el paralelismo a nivel de instrucción, tales como la ejecución de instrucciones fuera de orden y el uso de procesadores superescalares.
- Latencia aparente de las instrucciones: variable 1~100 ciclos. Por ejemplo, si una instrucción calcula una raíz cuadrada y la siguiente instruccion necesita saber el valor, el procesador se tiene que esperar hasta que el resultado esté listo.
- Predicción de saltos muy sofisticada.
- Memorias caché muy grandes, casi la mitad de los transistores del chip. Las cachés L3 andan ya por los 2~4MB.
Su diseño se centra en conseguir el mayor número de instrucciones ejecutadas por segundo para un único hilo de ejecución. Es por ello que necesitan la predicción de saltos y la extracción de paralelismo a nivel de instrucción.
Las GPUs, por el contrario, asumen que la tarea que quieres resolver en ellas se puede dividir en una infinidad de hilos de ejecución independientes. Si tienes tantos hilos, ¿para qué dedicar transistores a predecir la dirección de los saltos? Cuando ves una instrucción de salto puedes simplemente pasar a ejecutar otro hilo distinto hasta que sepas si el salto se produce o no. Por el mismo argumento no necesitas grandes cachés: si un hilo A ha sufrido un fallo de caché, en vez de tener el procesador esperando sin hacer nada, puedes pasar a ejecutar otro hilo cualquiera B hasta que los datos solicitados por A hayan llegado de la RAM. En ese momento, A pasará otra vez a la cola de hilos listos para ejecutarse.
Ahora no os sorprenderá ver las características de una GPU moderna:
- Núcleos/"cores": 200~800 y creciendo.
- Unidades aritmético-lógicas: escalares (NVidia) o SIMD (AMD).
- Frecuencia de reloj: 500~750 MHz.
- Hilos simultáneos por núcleo: decenas. El cambio de un hilo a otro es transparente y no tiene ningún coste.
- Ejecución de instrucciones en orden.
- Los procesadores no son superescalares.
- Latencia aparente de las instrucciones: 1 ciclo siempre. Lo que pasa en realidad es que mientras un cálculo está llevándose a cabo en un hilo, otros hilos se están ejecutando simultáneamente.
- Predicción de saltos inexistente.
- Memorias caché pequeñas. Es muy difícil obtener cifras precisas googleando.
La ejecución de cada hilo individual es mucho más lenta que en una CPU, pero como se están ejecutando miles de hilos concurrentemente, la cantidad de instrucciones por segundo en conjunto es brutal.
El arbitraje de los hilos no lo hace el sistema operativo (no hay tal cosa en una GPU), sino que lo realiza un bloque hardware diseñado específicamente para esa tarea.
Hay otras cualidades que separan a las GPUs de los procesadores de propósito general. Por ejemplo, tienen bloques hardware para realizar algunas tareas que son muy costosas de realizar mediante software, pero que por otra parte son sólo útiles cuando estás computando gráficos 3D.
En resumen, las GPU funcionan muy bien para resolver problemas extremadamente paralelos, sobre todo si requieren mucho cómputo en relación a la cantidad de datos. Sus pequeñas cachés no dan abasto si hay que acceder a memoria con mucha frecuencia.
Aunque en esta entrada he subrayado las diferencias entre estas dos arquitecturas, lo cierto es que están convergiendo. Creo que de aquí a cinco años, la mayoría de los ordenadores que se venderán tendrán en su interior chips híbridos. Probablemente tendrán de uno a cuatro núcleos tipo CPU y unos cientos de núcleos tipo GPU con sus necesarios bloques hardware auxiliares para hacer gráficos 3D. Con ello se conseguiría un buen rendimiento en tareas con poco paralelismo y al mismo tiempo la capacidad de resolver problemas muy paralelos en poco tiempo.