domingo, 27 de diciembre de 2009

Programación del Cell

Como ya hemos visto, el procesador Cell tiene una arquitectura que exige un esfuerzo extra al programador a la hora de adecuar el programa a las peculiaridades del hardware. Esto es importante si se quiere aprovechar la capacidad del procesador y para ello hay varios modelos de programación que se pueden seguir.

En primer lugar se puede programar para Cell como si fuera un procesador de propósito general, al tener un núcleo PowerPC con VMX. Este modelo es muy sencillo pero obviamente desaprovecha la mayor parte de la potencia de cálculo del procesador. Sin embargo para tareas en las que esta potencia no es realmente necesaria compensa el menor tiempo y coste de desarrollo.

El siguiente paso en cuanto a complejidad consiste en utilizar un SPE para realizar ciertas tareas concretas. Mientras que el programa se ejecuta en el PPE, parte del algoritmo puede que se beneficie de la potencia de cálculo de los SPE. En ese caso se programa y compila para un SPE y desde el PPE se encarga de configurar el procesador para que se ejecute cuando haga falta. Este modelo de programación utiliza los SPEs como una ayuda en momentos puntuales. Esto introduce una dificultad añadida que es la gestión de la memoria. Cada SPE tiene una memoria local de 256 KB y puede acceder a la memoria principal mediante DMA. En caso de que los datos y el código no quepan en la memoria local hay que incluir código que gestione los datos de la memoria local y el acceso a memoria principal para que el SPE no se quede parado a la espera de los datos que necesite.

Para aprovechar al máximo los recursos que nos ofrece el procesador hay que utilizar modelos de programación paralela. Los modelos clásicos pueden adaptarse a este procesador, como por ejemplo el modelo de memoria compartida. Los SPE pueden procesar los datos en memoria compartida mientras que el PPE les facilita los servicios propios del sistema operativo, como acceso a la memoria, a sistemas de entrada/salida, etc.



Además del modelo de memoria compartida hay otros, como establecer una cola de trabajos de forma que se va repartiendo trabajo a los SPEs según van solicitándolo al unirse a dicha cola. Esta cola la gestiona el PPE y va asignando datos a procesar y espacio en memoria en la que dejar los resultados a los SPEs que estén bloqueados en la cola. Otra posibilidad es formar un pipeline con los SPEs, de manera que van realizando parte del trabajo y pasando el resultado al siguiente SPE que lo continuará, al estilo de una cadena de montaje.

Teniendo memoria compartida, esta puede utilizarse para implementar los métodos habituales de comunicación entre procesos como mutexes, semáforos o paso de mensajes y aprovecharlos para ejecutar varios hilos en paralelo en distintos SPEs.

Otra forma de gestionar los recursos del procesador es mediante el propio sistema operativo, en lugar de hacerlo el desarrollador de la aplicación. Esto se puede realizar de 2 maneras. La primera consiste en considerar los SPEs como un recurso al que se puede acceder representándolo mediante un sistema de ficheros. La segunda estrategia consiste en que el sistema operativo sea capaz de diferenciar entre hilos que deben ejecutarse en el PPE e hilos para los SPEs. En este caso es el kernel del sistema operativo el que se encarga de planificar los hilos y enviarlos a ejecutar a la unidad que corresponda del procesador. El programador puede crear y destruir hilos o tareas para los SPEs mediante llamadas al sistema. Este modelo es el que utiliza el kernel de Linux.

Patxi Astiz

miércoles, 23 de diciembre de 2009

Cell vs. Xenon

La última generación de consolas ha traído 2 procesadores que han introducido cambios importantes en la forma de diseñar procesadores de proposito general que se ha mantenido hasta ahora. Por una parte, el procesador de la Xbox360 (Xenon), al que hemos dedicado una entrada en esta blog y por otra el procesador Cell desarrollado por IBM, Sony y Toshiba. Este es el aspecto que tiene el Cell:


Como puede verse el diseño es muy diferente al del procesador Xenon, ya que aunque tiene varios núcleos, estos son heterogéneos. Sin embargo, el objetivo de los 2 procesadores no es tan diferente.

El PPE (Power Processor Element) del procesador Cell es muy parecido a un núcleo del Xenon. Ejecuta instrucciones en orden y puede ejecutar 2 hilos simultáneamente. También está basado en la tecnología PowerPC y funciona a la misma velocidad. También tiene la tecnología VMX. La memoria caché del PPE también tiene 2 niveles, con 32 KB para instrucciones y 32 KB para datos en la caché de nivel 1 y 512 KB en la de nivel 2. Como se puede ver este núcleo es bastante similar a un núcleo del procesador Xenon.

El diseño del procesador de la Xbox está pensado para ejecutar aplicaciones que procesan grandes cantidades de datos y para ello se han dispuesto 3 núcleos que funcionan a velocidades de reloj elevadas con unidades vectoriales potentes pero que no dejan de ser procesadores de propósito general. En el Cell el objetivo es el mismo, proporcionar mucha potencia de calculo para este tipo de aplicaciones pero para ello se han añadido al diseño procesadores mucho más especializados. Estos son los llamados SPE (Synergistic Processing Elements).


Los núcleos SPE tienen ciertas características que los hacen especialmente potentes para algunos cálculos a cambio de simplificar al máximo su diseño. Cada uno de estos nucleos tiene 128 registros de 128 bits y es capaz de hacer 4 operaciones de coma flotante de precisión simple en un ciclo de reloj. Un SPE tiene una memoria local de 256 KB , ejecutan las instrucciones en orden, carecen de caché y de predicción de saltos. Una unidad tiene un pico teórico de 25.6 GFLOPS, sin embargo el rendimiento con (por ejemplo) código con muchos saltos condicionales se reduce drásticamente. Este tipo de código debería ejecutarse en el PPE.

En ambos casos los procesadores dejan bastante responsabilidad al programador y al compilador que deben asegurarse de generar código paralelizado que utilice los recursos del procesador en cuestión adecuadamente. En el caso del Cell el hecho de tener núcleos heterogéneos y en el caso de los SPEs muy especializados en cierto tipo de cálculos, obliga a un esfuerzo mayor a la hora de decidir cómo realizar la división y adaptación de un programa en hilos, teniendo en cuenta en qué tipo de núcleo deben ejecutarse. Mientras tanto en el Xenon todos los núcleos son iguales y la división y adaptación es más sencilla. A cambio, el procesador Cell puede conseguir un rendimiento altísimo si se programa adecuadamente para él.

Patxi Astiz

jueves, 17 de diciembre de 2009

Xenon

Xenon es el procesador de la consola de Microsoft Xbox360. El diseño de este microprocesador es obra de IBM y aunque está basado en la arquitectura PowerPC, hay varios cambios que se han hecho pensando en el tipo de tareas que el procesador de una consola suele realizar. Este es el aspecto del procesador Xenon:



En un videojuego o en aplicaciones multimedia en general, es muy habitual el realizar procesado más o menos sencillo y uniforme sobre una gran cantidad de datos que normalmente son (casi) secuenciales. Por esto se ha diseñado un procesador con un pipeline de 21 etapas. Esto permite que la frecuencia de reloj sea muy alta y en el caso de ejecutar código sin muchos saltos condicionales y en el que se puede prever y leer los datos que se necesitarán con suficiente antelación, el rendimiento del procesador crece.

Otra característica importante de este procesador es que se ha intentado aprovechar el paralelismo a nivel de hilo en vez de la estrategia más clásica de explotar el paralelismo a nivel de instrucción. Esto se traduce en que el procesador tiene 3 núcleos, cada uno de los cuales puede ejecutar 2 hilos. Además estos núcleos no tienen capacidad para ejecutar instrucciones fuera de orden ni especulan, haciendo que cada núcleo sea más sencillo, consuma menos y ocupe menos espacio en el circuito integrado. Ahora el programador y el compilador son los responsables de asegurar el paralelismo en la ejecución en lugar del procesador. Otra consecuencia de este planteamiento es que en general no se replican las unidades funcionales, como es habitual en muchos procesadores actuales, ya que como se acaba de explicar, se busca que sean instrucciones de diferentes hilos que se ejecutan en distintos núcleos las que se ejecutan simultáneamente, en lugar de que un núcleo reordene las instrucciones para conseguir el paralelismo. Estas son las unidades que tiene cada núcleo:

  • 1 unidad de enteros
  • 1 unidad de coma flotante
  • 1 unidad de salto
  • 1 unidad load-store
  • 2 unidades VMX-128


Como se puede ver hay una excepción y se ha replicado la unidad VMX (con 128 registros) que es la encargada de ejecutar operaciones vectoriales sobre los datos. El motivo es que es de esperar que se ejecuten aplicaciones en las que hay paralelismo de datos (gráficos en 3D, cálculos cálculos físicos) que se ven muy beneficiadas por estas unidades.

Respecto a la jerarquía de memoria en el procesador, está dividida en 2 niveles de memoria caché. El primero al ser un procesador segmentado, consiste de una caché de instrucciones de 32 KB y otra de datos del mismo tamaño. El 2º nivel de caché tiene una capacidad de 1 MB. En comparación con procesadores de propósito general de PC, esta memoria es algo escasa, pero tiene su justificación en las limitaciones técnicas y en que juegos y aplicaciones similares no suelen beneficiarse de memorias caché muy grandes, ya que habitualmente leen datos y realizan cálculos con ellos, pero no suelen reutilizarlos. De hecho el desarrollador puede elegir deshabilitar la caché si en algún momento el código no lo necesita. También puede utilizarse para intercambiar datos con la GPU, ya que esta tiene acceso a la caché y enviar de manera muy eficiente información geométrica generada en el procesador.

En resumen, el diseño de este procesador, aunque basado en un PowerPC de propósito general, se ha modificado sustancialmente favoreciendo a las aplicaciones que realizan cálculos sobre grandes cantidades de datos, como multimedia o 3D intensivo. Estas son las partes más costosas a la hora de ejecutar un videojuego moderno en tiempo real, por lo que aunque el código relacionado con la lógica del juego o la inteligencia artificial se vea perjudicado, en conjunto el rendimiento obtenido es bueno.

Patxi Astiz