Rigel en Inglaterra

domingo, agosto 24, 2008

CPU versus GPU

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.

4 Comments:

  • Vaya, me ha gustado la explicación. No sabía que las GPUs tuvieran muchos cores, y he concluído, a grandes rasgos, que esto es como los computadores de memoria compartida pero variando la escala y con mucha independencia entre tareas.

    Estaba pensando en posibles problemas que se adapten a esta arquitectura (todo el mundo sabe que las ideas valen lo que los problemas que se pueden resolver con ellas), y se me ha ocurrido el juego de la vida de Conway, haciendo que cada celda sea un "minihilo". ¿Es un buen ejemplo o las dependencias entre las casillas harían que no fuera adecuado?

    By Blogger Gabriel, at 12:35 a. m.  

  • Hey, hola!

    Sí que tienen un aire a superordenadores de memoria compartida.

    Como puedes imaginarte, he simplificado mucho la arquitectura. En realidad, manojos de 16-32 hilos pueden compartir unos pocos KB de RAM con posibilidad de sincronización entre hilos del mismo manojo.

    El juego de la vida es perfectamente resoluble. En ese caso lo que tendrías son dos copias del tablero: la iteración pasada y la iteración presente. El cómputo de cada célula en la iteración presente sólo depende de los vecinos de la iteración pasada, por lo que en este caso ni siquiera necesitas sincronización entre vecinos ni nada.

    Otros problemas que resuelven bien son cosas tales como edición de vídeo, filtros de convolución, resolución de sistemas de ecuaciones, álgebra lineal en general, FFTs, etc.

    El nuevo lenguaje/librería OpenCL es muy interesante para programar GPUs porque te permite expresar explicitamente las dependencias entre tareas. El plan es tener el estándar ratificado en noviembre, por lo que suma tres o cuatro meses y tendrás implementaciones disponibles.

    By Blogger Rigel, at 1:06 p. m.  

  • Muy interesante post :-). En cuanto a lo de las canas, no te preocupes, no quiere decir nada... te lo dice una que las tiene desde los 15 años y a quien últimamente se le reproducen como champiñones 8-). ¡Anda y llévate a cenar a Mayu a algún sitio chulo por su cumple y verás como se te olvida! ;-)

    By Blogger _luara_, at 11:07 a. m.  

  • _luara_, gracias. Eres muy amable :)

    Un abrazo,

    Rigel

    By Blogger Rigel, at 2:18 p. m.  

Publicar un comentario

<< Home