Download La arquitectura “Cell Broadband Engine”

Document related concepts
no text concepts found
Transcript
La arquitectura
“Cell Broadband Engine”
Miguel Ángel Expósito Sánchez
Índice
Introducción ………………………………………………………………………………3
Arquitectura ……………………………………………………………………………. 4
Power Processor Element ……………………………………………………………… 6
Synergistic Processor Element ………………………………………………………… 6
Element Interconnect Bus ……………………………………………………………… 7
Consideraciones sobre ancho de banda ………………………………………………… 8
Modelos de programación ……………………………………………………………… 10
Stream processing ……………………………………………………………………… 10
Cola de tareas …………………………………………………………………………… 10
Multitarea auto-gestionada ……………………………………………………………… 11
Escalabilidad …………………………………………………………………………… 11
Pruebas oficiales y resultados ………………………………………………………….. 12
Multiplicación de matrices …………………………………………………………….. 12
Programación del Cell ……………………………………………………………………14
Mecanismos de comunicación ………………………………………………………… 14
Mailboxes ……………………………………………………………………………… 14
Ejemplo de programación ……………………………………………………………… 15
DMA …………………………………………………………………………………… 16
Ejemplo de un programa completo …………………………………………………….. 18
Simulación del Cell ………………………………………………………………………20
Tiempos de comunicación entre núcleos en Cell ………………………………………. 21
Implementación del conjunto de Mandelbrot en Cell ……………………………………23
Obteniendo rendimiento del Cell ……………………………………………………….. 25
Precauciones …………………………………………………………………………… 26
Multicomputación con Cell ………………………………………………………………27
Conclusiones …………………………………………………………………………… 29
Bibliografía ………………………………………………………………………………30
2
Introducción
El procesador Cell es el resultado del esfuerzo conjunto entre Sony, Toshiba e
IBM (STI) para desarrollar una nueva arquitectura de última generación que además es
el corazón de la consola PlayStation 3.
Se trata de una arquitectura paralela de última generación.
Su desarrollo ha durado más de 3 años y se han invertido en él más de 400
millones de dólares. Apareció públicamente en 2005.
Ha sido diseñado para realizar trabajos de cálculo intensivo, y aplicaciones
llamadas de “banda ancha”, de hecho su nombre completo es Cell Broadband Engine
(Motor de Banda Ancha Cell), entre las que se incluyen videojuegos, descompresión de
audio/vídeo, y otros contenidos digitales.
Es un procesador altamente escalable y puede utilizarse en multitud de
dispositivos. Esta es la tecnología que las tres compañías desarrolladoras pretenden usar
para inundar el mercado con dispositivos de diversos tipos, posibilitando que sistemas
con, en principio poca potencia de cálculo puedan competir con superordenadores
científicos.
Las características concretas de la arquitectura se publicaron en la ISSCC
(Conferencia Internacional de Circuitos en Estado Sólido) en el año 2005 en San
Francisco. Sus principales características son las siguientes:
-
Arquitectura Multi-hilo y Multi-núcleo.
Gran ancho de banda desde/hacia la memoria.
Manejo de recursos en tiempo real.
Sistema DRM (Administrador de Derechos Digitales) integrado en el propio
chip.
Frecuencia de 4.2 GHz (la versión instalada en la PS3 funciona a 3.2 GHz).
Tensión de trabajo de 1.3 Voltios
Temperatura de operación de 85 ºC con un disipador.
8 procesadores esclavos independientes llamados SPE y 1 procesador
maestro llamado PPE.
Rendimiento máximo teórico de 204.8 GFlops
Se puede decir por tanto que se trata de un sistema multiprocesador
empotrado en un chip.
El procesador Cell también se utiliza en algunos de los nuevos servidores de
la empresa Mercury Systems, que utilizan dos de ellos.
Sus otras exóticas características como el interfaz de conexión con memorias de
alta velocidad XDR de Rambus, y su bus de interconexión de elementos de proceso
“Element Interconnect Bus” (EIB) posicionan al Cell como futuro candidato a
aplicaciones de supercomputación, dado su alto rendimiento en operaciones de punto
flotante. IBM ha lanzado además tarjetas equipadas con procesadores Cell (IBM Blade
Center QS20) que pueden ser instaladas en los Mainframe IBM que hay actualmente en
3
producción, permitiéndoles actuar como servidores para aplicaciones de muy alta carga
como los juegos multijugador masivos online.
Arquitectura
El procesador Cell está compuesto por un procesador de propósito general
principal IBM PowerPC, llamado Power Processor Element, o PPE, y de 8
coprocesadores esclavos llamados muy rimbombantemente Synergistic Processor
Engine, o SPE.
En la PlayStation 3, uno de los PPE está deshabilitado debido al complicado
proceso de fabricación, que resulta en un porcentaje no muy alto de procesadores Cell
plenamente funcionales (con todos sus PPE operativos), lo que ayuda a reducir los
4
costes de fabricación, y otro PPE está reservado al sistema operativo, con lo cual quedan
un total de 6 SPEs disponibles para el programador.
Los elementos de proceso se interconectan entre sí, con el controlador de
memoria RAM y con la CPU principal mediante el Element Interconnect Bus (EIB).
Una memoria caché de nivel 2 proporciona acceso rápido a las instrucciones y datos
usados recientemente. El controlador de memoria XDRAM permite la conexión del Cell
a una memoria de alta velocidad con un ancho de banda de hasta 25.6 Gb/s.
El módulo RamBus FlexIO proporciona al sistema 12 canales de entrada / salida
con un ancho de banda total de 36.4 GB/s de salida y 26 GB/s de entrada, además de
posibilitar la escalabilidad permitiendo la comunicación entre SPEs que se encuentren
físicamente en distintos chips. En PlayStation 3 se utiliza para la comunicación con la
GPU y con el procesador de E/S para acceso a los puertos USB, ethernet, etc….
5
Se fabrica en tecnología de 65 nm, y tiene 234 millones de transistores. IBM ha
lanzado varias patentes relacionadas con nuevas tecnologías empleadas para la
fabricación del Cell.
Power Processor Element
El PPE es un procesador de propósito general de doble núcleo basado en la
arquitectura PowerPC (Performance Optimization With Enhanced RISC
Parallel Computing) de IBM, que actúa como controlador para los SPEs, tiene una
caché de instrucciones de 32KiB y otra de datos del mismo tamaño. Es una arquitectura
RISC con instrucciones de 32 bits pero 64 registros de propósito general de 64 bits,
además de una unidad Altivec de 128 bits que permite operaciones en doble precisión
en punto flotante, lo que proporciona un rendimiento de 6.4 GFlops/s adicional cuando
el procesador trabaja a 3.2 GHz. Es un procesador vectorial tipo SIMD que puede
ejecutar sistemas operativos estándar para PowerPC debido a la gran similitud de su
juego de instrucciones con los procesadores existentes, pero internamente es más
sencillo que un PowerPC tradicional, ya que su circuitería de predicción de saltos ha
sido simplificada y carece de ejecución fuera de orden debido a restricciones en cuanto
al consumo eléctrico y de número de transistores, que se ha preferido dedicar a los SPE.
Synergistic Processor Element (SPE)
Los SPE tienen un diseño relativamente sencillo y proporcionan 25.6 GFLOPS
cada uno cuando trabajan a 3.2 GHz, se componen de una SPU (Synergistic Processing
Unit), un controlador de Flujo de Memoria (MFC) formado por colas, que es capaz de
iniciar transacciones DMA, un controlador de acceso directo a memoria (DMA), una
unidad de gestión de memoria (MMU) y un interfaz de bus.
Son procesadores RISC superescalares con organización MIMD de 128 bits.
Cada SPE tiene 256KiB de memoria interna SRAM (de gran velocidad pero gran
ocupación de silicio) para almacenar código y datos llamada “Local Storage”
(Almacenamiento Local), que es visible por el PPE y se puede acceder directamente por
software. Tampoco tienen ejecución fuera de orden.
Esta memoria no es caché ya que ni es transparente al software ni contiene
circuitería que predice los datos que van a ser accedidos (gestión de líneas de caché,
etc…), sino que simplemente almacena el programa que debe ejecutar el SPE y los
datos con los que debe trabajar.
Tiene un banco de 128 registros de 128 bits cada uno.
6
El SPE no puede acceder directamente a la memoria principal del sistema, sino
que se utiliza un sistema de direccionamiento virtual de 64 bits que es pasado al
controlador de flujo de memoria, el cual utiliza una transacción DMA para copiar los
datos solicitados a la memoria de almacenamiento local del SPE. Esto tiene la
desventaja de que cuesta bastante tiempo iniciar una transferencia DMA pero en cuanto
empiezan a fluir los datos tiene un rendimiento aceptable.
Element Interconnect Bus (EIB)
El bus de comunicación interno EIB es el encargado de conectar los distintos
elementos que componen el chip: El procesador principal (PPE), el controlador de
memoria principal (MIC), los 8 coprocesadores SPE, y dos interfaces de E/S que se
pueden conectar fuera del chip, lo cual hace un total de 12 participantes.
El EIB también incluye una unidad de árbitro de bus.
Dista mucho de ser un bus tradicional que comparten los elementos conectados a
él, sino que su funcionamiento se aproxima más al de un crossbar.
7
Su estructura interna está implementada como un anillo circular (lo que es
óptimo para trabajar con flujos de datos, pero no para las comunicaciones punto a
punto) compuesto de canales de 16 bytes unidireccionales que rotan por pares (uno en
una dirección y el otro en la opuesta). Cuando los patrones de tráfico lo permiten, cada
canal puede realizar hasta tres transacciones concurrentemente. Ya que el EIB funciona
a la mitad de la frecuencia de reloj del sistema, la tasa de transferencia del canal es de
16 bytes cada dos ciclos de reloj del sistema, pero cuando se utiliza la máxima
concurrencia, como se realizan 3 transacciones por cada canal, en teoría el pico
instantáneo de ancho de banda del EIB es de 96 bytes por ciclo de reloj: (12
transacciones concurrentes * 16 bytes cada una / 2 ciclos de reloj por transferencia),
pero en la realidad esto no es así porque el árbitro de bus impone algunas restricciones.
Resulta interesante comprobar que como hay un máximo de 12 participantes, los datos
pasan de nuevo por el mismo origen cada 12 pasos, y por lo tanto la distancia máxima
entre dos participantes es de 6, por lo que el bus no permite enviar datos a más de 6
participantes de distancia, sino que obliga a que tomen el camino más corto viajando en
el otro sentido por el anillo adecuado. El programador es el responsable de programar
las transferencias de mayor volumen de datos cuando el bus esté más disponible para
trabajar en modo de concurrencia máxima. Como puede verse por tanto, en realidad su
funcionamiento también dista bastante del de un crossbar ya que el bus no establece un
canal de comunicación dedicado entre los participantes de una comunicación. Se optó
por la estructura en anillo porque no había suficiente espacio en la oblea de silicio del
chip, pero el resto del diseño está preparado para posibilitar la sustitución del EIB por
un switch de tipo crossbar sin más modificaciones. Además según los desarrolladores,
para el tipo de aplicaciones para el que se pretende usar el Cell, esta estructura en anillo
tiene un ancho de banda bastante impresionante.
Consideraciones sobre ancho de banda
Cada SPE puede transmitir y recibir simultáneamente 16 bytes en cada ciclo de
bus puesto que tiene un solo puerto de lectura y otro de escritura, ambos de 16 bytes, de
forma que si el procesador está trabajando a 3.2 GHz:
16bytes * (3.2GHz/2) = 25.6 Gb/s en ambos sentidos
8
Como el número máximo de participantes es 12, el ancho de banda máximo
teórico que puede transportar el EIB sería:
25 Gb/s * 12 participantes = 307.2 Gb/s
Pero como ya se ha comentado antes, hay otros aspectos técnicos que limitan la
aceptación de paquetes en el bus. El árbitro decide que anillo asignar a que elemento
que desea comunicarse y cuando. No asignará anillos que estén en uso y cuya
comunicación actual interfiera con la que se desea realizar, es decir, si el segmento por
el cual ya están circulando datos se solapa con el que se desea establecer. A todo esto
hay que añadir la existencia de un sistema basado en créditos para la asignación del bus
a cada participante.
Ejemplo de 8 transacciones simultaneas.
Existe cierta confusión respecto al ancho de banda real, ya que el mecanismo de
coherencia de datos, el arbitrado del bus, la generación de interrupciones y el
tratamiento de fallos de página no está muy descrito en la documentación hecha pública
por IBM, pero los desarrolladores lo tasan en 204.8 GB/s
El controlador de memoria XDR que proporciona acceso a la memoria principal,
externa al chip está montada en configuración Dual Channel, y proporciona un ancho de
banda máximo teórico de 25.6 GB/s, de forma que en el mejor caso un SPE puede leer
datos de la memoria principal a la mayor velocidad permitida (cuando se trabaja a
3.2GHz).
9
Modelos de programación
Dada la flexible arquitectura del Cell, se han establecido varios modelos para su
programación, para explotar el paralelismo inherente a cada aplicación específica.
Stream processing
Es el nombre comercial que se le ha dado a este modelo de programación,
basado en flujo de datos, y consiste en alinear varios SPEs en cascada, para que cada
uno de ellos ejecute parte de un proceso complejo, que pueda ser segmentado, como es
el
caso
de
la
decodificación
MPEG.
La estructura en anillo del EIB favorece el uso de este modelo de programación
ya que cada SPE se comunica con sus siguientes. El primer SPE puede solicitar datos
comprimidos desde la memoria principal mediante DMA, y al recibirlos realizar la
primera fase del proceso, una vez que tenga sus resultados se pueden enviar a los
siguientes SPEs. El último SPE, una vez tenga sus resultados puede enviarlos
directamente a la unidad encargada de aprovecharlos (el frame buffer de video, el buffer
de audio del sistema de sonido, o simplemente almacenarlos en su local store para que
sean leidos por el PPE).
De esta forma las fases del trabajo se realizan en paralelo y se mantiene libre el
PPE, que estará ejecutando el sistema operativo y otras tareas.
Muchos expertos coinciden en que un nombre más apropiado para este modelo
de programación habría sido “Pipelined processing”, o procesamiento segmentado.
Cola de tareas
Este modelo permite la realización de diversas tareas en paralelo, tengan o no
que ver. Se basa en que el PPE mantiene una lista de tareas, y se las va asignando
dinámicamente a cada SPE según vayan quedando libres. Cada SPE ejecuta un mini10
kernel cuya misión es recibir un trabajo, ejecutarlo y comunicar eventos y resultados al
PPE.
Multitarea auto-gestionada
Es el modelo en el que está trabajando Arnd Bergmann, de IBM para Linux,
consiste en abstraer cada SPE como un dispositivo virtual en el directorio /dev de
Linux, y permite comunicarse con él mediante llamadas al sistema. El programador es
el responsable de toda la sincronización entre SPEs mediante hilos y semáforos, como si
de programación con hilos se tratase. Los SPEs utilizan memoria compartida para todas
las tareas en esta configuración.
Escalabilidad
Como se ha comentado previamente, los Cell se pueden conectar con otros para
formar sistemas mayores, dado que implementan el sistema FlexIO de RAMBUS, la
comunicación es una extensión del EIB, y permite de forma relativamente transparente
el intercambio de datos entre PPEs, y SPEs de diferentes chips.
Sistema Cell básico
Conexión en cascada
11
Conexión mediante switch
Pruebas oficiales y resultados
El equipo de pruebas de IBM hizo públicos los resultados de ejecución de varias
pruebas ejecutadas sobre el procesador Cell.
Multiplicación de matrices
La prueba consiste en la multiplicación de matrices de 512x512 y
1024x1024 empleando para ello varios SPE.
No se dan muchos detalles sobre la implementación, entre ellos que el
reparto se realiza por bloques de orden M < N (donde N es el lado de la matriz
cuadrada original).
Además se han explotado las instrucciones SIMD cuádruples (usando 4
valores de 32 bits en registros de 128) de suma y multiplicación (en una única
instrucción), con lo que se realizaban 4 datos * 2 operaciones = 8 operaciones en
coma fija por ciclo, y también se ha utilizado un doble buffer para mantener un
flujo de datos constante en el SPE, debido a las pequeñas paradas del DMA y del
bus. También se han aplicado técnicas de optimización clásicas como
desenrollado de bucles y segmentación software.
Los resultados de rendimiento (en GFlops) fueron los siguientes para 1,
2, 3, 4, 5, 6, 7, y todos los SPE:
12
Ya que las operaciones que se realizan en cada bloque son
independientes de los otros bloques, existe un alto grado de paralelismo, con lo
que la productividad aumenta casi linealmente con el número de SPEs. Con 8
SPEs se llega a 201 GFlops, muy cerca del máximo teórico de 204.8
(recordemos que cada SPE proporciona 25.6 GFlops), sin la intervención del
PPE.
La misma prueba ejecutada en un Pentium 4 con SSE3 produjo un
resultado de 25.6 GFlops, lo que demuestra que para este problema concreto, el
uso del Cell ha mejorado considerablemente la productividad (al menos por un
factor de 8).
Todos estos valores están referidos a cálculos en simple precisión.
13
En la gráfica se muestran los resultados de ejecución paralela de algoritmos de
cálculo intensivo con diferentes tipos de datos sobre diferentes arquitecturas.
Programación del Cell
El Cell se puede programar en varios lenguajes especialmente usados en
paralelismo, como fortran, pero el más común y extendido es C.
IBM proporciona el “IBM Cell SDK para Linux”, y un parche para el kernel de
Linux que permite utilizar las funciones específicas del Cell (buffers de E/S,
comunicación con los SPEs, etc…).
La PlayStation 3 permite la ejecución del sistema operativo Linux, en diversas
distribuciones, pero concretamente la YellowDog Linux, desarrollada conjuntamente
por IBM, Sony y YellowDog para dicha plataforma, con lo que junto con el IBM Cell
SDK, la PS3 se convierte en un kit de desarrollo basado en Cell.
El código que ejecutan los SPEs y el código que ejecuta el PPE se compilan con
compiladores diferentes puesto que son arquitecturas distintas, y una vez creados los
archivos objeto, con la herramienta embedspe se enlaza todo en un archivo ejecutable.
Cada fichero objeto de código SPE exporta como global un manejador del programa
que permite al PPE cargar el código en cualquier SPE.
Mecanismos de comunicación
Existen dos tipos de direcciones, la dirección efectiva (ea), que es de 64 bits y
abarca todo el mapa de memoria. La memoria RAM, los dispositivos de E/S, el Local
Store de cada SPE y todo lo demás está mapeado en la ea.
Para el direccionamiento local a nivel de SPE se utiliza una dirección reducida
de 32 bits (18 en la PS3), llamada lsa (Local Store Address), que comprende sólo los
256Kb de memoria, y los registros del MFC, y demás.
Mailboxes
14
Los mailboxes (buzones) se usan para la comunicación entre PPE y SPE.
Se usan para: sincronización, notificación de errores, comunicación de propósito
general y monitorización del estado del SPE.
Cada controlador de flujo de memoria (MFC) implementa 3 colas de 32
bits cada una:
- PPE mailbox
o SPE => PPE
o Profundidad de 1 elemento.
- PPE interrupt mailbox
o Igual que la anterior, pero el PPE recibe una interrupción cuando el
SPE escribe en la cola.
- SPE mailbox
o SPE <= PPE
o Profundidad de 4 elementos (cola FIFO).
o Se puede sobrescribir, con lo que se pueden perder datos.
Cuando se intenta escribir en un mailbox lleno, el SPE permanece
detenido hasta que se libera espacio mediante la lectura desde el otro
extremo.
Cuando se intenta leer de un mailbox vacío, el SPE permanece
detenido hasta que se escriba algo en él.
Para evitar las detenciones se puede consultar el estado del mailbox
antes de realizar las operaciones para asegurarse de su disponibilidad.
No hay ningún orden entre las interrupciones y los mensajes
recibidos.
Ejemplo de programación
Escritura de un valor en el “PPE mailbox”, comprobando primero la
disponibilidad de la cola (este código lo ejecuta un SPE).
unsigned int mb_value;
do {
/*
* Hacer trabajo util aqui
*/
} while (!spu_readchcnt(SPU_WrOutMbox)); // 0 =>
lleno, hacer algo util
spu_writech(SPU_WrOutMbox, mb_value);
Lectura de un valor de la cola del “PPE mailbox” del SPE desde el
PPE (este código lo ejecuta el PPE).
void *ps = spe_get_ps(speid); // Llamada el sistema
para devolver la dirección de memoria del controlador
de la cola del SPE
unsigned int mb_status;
unsigned int new;
unsigned int mb_value;
do {
mb_status = *((volatile unsigned int *)(ps +
SPU_Mbox_Stat));
new = mb_status & 0x000000FF;
} while ( new == 0 );
15
/*
* Ejecutar la instrucción eieio para asegurar
* que la lectura del registro de estado de la cola
* se ha ejecutado antes que la lectura del valor
*/
__asm__(“eieio”);
mb_value = *((volatile unsigned int *)(ps +
SPU_Out_Mbox));
Escritura de 4 valores desde el PPE en la cola de entrada del SPE.
void *ps = spe_get_ps(speid);
unsigned int j,k = 0;
unsigned int mb_status;
unsigned int slots;
unsigned int mb_value[4] = {0x1, 0x2, 0x3, 0x4};
do {
/*
* Sondear el registro de estado del mailbox para
comprobar que hay almenos un slot disponible
*/
do {
mb_status = *((volatile unsigned int *)(ps +
SPU_Mbox_Stat));
slots = (mb_status & 0x0000FF00) >> 8;
} while ( slots == 0 ); // mientras esté lleno
/* Ejecutar la instrucción eieio para segurar que la
* lectura del registro de estado de la cola se ha
* ejecutado antes que la lectura del valor
*/
__asm__(“eieio”);
for (j=0; j<slots && k < 4; j++) {
*((volatile unsigned int *)(ps +
SPU_In_Mbox)) = mb_value[k++];
}
} while ( k < 4 );
DMA
Existe todo un sistema de control DMA gestionado por los MFC de los
SPE dirigido por numerosos comandos para hacer muy diversos tipos de
transferencia.
Los tamaños de la transacciones pueden ser de 1, 2, 4, 8 y n*16 bytes
(siendo n entero), con un límite máximo de 16 KB por transacción.
Cada SPE tiene una cola de comandos que permite encolar hasta 16
solicitudes al controlador DMA.
El PPE tiene una cola similar de 8 elementos, teniendo preferencia las
transacciones iniciadas por los SPEs.
Cada solicitud DMA tiene asociado un tag de 5 bits que puede ser
utilizado como identificador para la recepción, o para monitorizar el estado de la
solicitud, al igual que el tag de las funciones de comunicación de MPI.
Un solo comando DMA puede causar la ejecución de una lista de
solicitudes (almacenadas en el local store de los SPE), las listas se pueden usar
para implementar scatter y gather. Una lista puede contener hasta 2000
solicitudes de transferencia.
16
Un método de DMA para maximizar el rendimiento consiste en el double
buffering, que consiste en definir dos buffers en el local store, de forma que se
solape el tiempo de computación con el tiempo de transferencia DMA.
Una de las funciones de llamada al sistema DMA para un spe es la
siguiente:
spu_mfcdma32(saddr,ea,size,tag_id,cmd)
Donde saddr es la dirección del local store del SPE
EA es la dirección efectiva (dirección del mapa de memoria que “ve” el
controlador DMA, y que permite especificar el destino / origen de la
transferencia a cualquier unidad del sistema o externa a el como la RAM
principal).
Size es el tamaño de la transferencia
Tag_id es el tag de la transferencia
Cmd es el comando a ejecutar
Ejemplo de programación de comunicación mediante DMA usando la
técnica del doble buffering:
/* Ejemplo de doble buffering empleando dos buffers en el Local Store
(B[0] y B[1].
* En este ejemplo, un array de datos empezando en la dirección
efectiva ‘ea’ es transmitido por DMA
* al local store del SPU en bloques de 4 KB para ser usados luego por
una funcion interna.
*/
#include <spu_intrinsics.h>
#include <spu_mfcio.h>
#define BUFFER_SIZE 4096
volatile unsigned char B[2][BUFFER_SIZE] __attribute__
((aligned(128)));
void double_buffer_example(unsigned int ea, int buffers)
{
17
int next_idx, idx = 0;
// Iniciar la primera transferencia DMA
spu_mfcdma32(B[idx], ea, BUFFER_SIZE, idx, MFC_GET_CMD);
ea += BUFFER_SIZE;
while (--buffers) {
next_idx = idx ^ 1; // inverter el indice del buffer
spu_mfcdma32(B[next_idx], ea, BUFFER_SIZE, idx, MFC_GET_CMD);
ea += BUFFER_SIZE;
spu_writech(MFC_WrTagMask, 1 << idx);
(void)spu_mfcstat(MFC_TAG_UPDATE_ALL); // Esperar a que acaben
las transferencias previas
use_data(B[idx]); // Usar los datos previos
idx = next_idx;
}
spu_writech(MFC_WrTagMask, 1 << idx);
(void)spu_mfcstat(MFC_TAG_UPDATE_ALL); // Esperar a que termine la
ultima transferencia
use_data(B[idx]); // Usar los ultimos datos
}
Ejemplo de un programa completo
El siguiente ejemplo utiliza descomposición funcional para que un SPE calcule
una sencilla fórmula:
distancia = velocidad * tiempo
Código que ejecutará el PPE:
#include <stdio.h>
#include <libspe.h>
//Esta variable global es un manejador creado por la utilidad
‘embedspu’
//y es una referencia al código generado para el SPE
extern spe_program_handle_t calculate_distance_handle;
//Esta estructura la usaremos para la comunicación con el SPE
typedef struct {
float speed;
//parametro de entrada
float num_hours; //parametro de entrada
float distance; //parametro de salida
float padding;
//valor que no se usa para que la estructura
// tenga un tamaño multiplo de 16 bytes
} program_data;
int main() {
program_data pd __attribute__((aligned(16)));
bytes para transferencia
//alineado a 16
//Obtener datos para enviar al SPE
printf("Introduce la velocidad a la que estas viajando en
km/h: ");
scanf("%f", &pd.speed);
printf("Introduce el numero de horas que has estado
conduciendo a esa velocidad: ");
scanf("%f", &pd.num_hours);
//USAR EL SPE PARA CALCULAR EL RESULTADO
18
//Creamos la tarea del SPE
//Esto cargará el código referenciado por el
‘calculate_distance_handle’ en el primer SPE disponible
speid_t spe_id = spe_create_thread(0,
&calculate_distance_handle, &pd, NULL,
-1, 0);
//Comprobar errores
if(spe_id == 0) {
fprintf(stderr, "Error creando el hilo SPE!\n");
return 1;
}
//Esperar a que se complete
spe_wait(spe_id, NULL, 0);
//FORMATEAR LA SALIDA PARA MOSTRARLA POR PANTALLA
printf("La distancia que has recorrido es %f km.\n",
pd.distance);
return 0;
}
Código que ejecutará el SPE
//Pull in DMA commands
#include <spu_mfcio.h>
typedef struct {
float speed;
float num_hours;
float distance;
float padding;
//parametro de entrada
//parametro de entrada
//parametro de salida
//valor que no se usa para que la estructura
//tenga un tamaño multiplo de 16 bytes
} program_data;
int main(unsigned long long spe_id, unsigned long long
program_data_ea, unsigned long long env) {
program_data pd __attribute__((aligned(16)));
int tag_id = 0;
//LEER LOS DATOS DE ENTRADA MEDIANTE DMA
//Iniciar copia
mfc_get(&pd, program_data_ea, sizeof(pd), tag_id, 0, 0);
//Esperar a que se complete
mfc_write_tag_mask(1<<tag_id);
mfc_read_tag_status_any();
//PROCESAR DATOS
pd.distance = pd.speed * pd.num_hours;
//ENVIAR RESULTADOS DE VUELTA
//Iniciar copia
mfc_put(&pd, program_data_ea, sizeof(program_data), tag_id, 0,
0);
//Esperar a que se complete
mfc_write_tag_mask(1<<tag_id);
mfc_read_tag_status_any();
return 0;
}
Para compilarlo y ejecutarlo debemos seguir estos pasos:
19
#Compilar el programa del SPE
spu-gcc spe_distance.c -o spe_distance
#Convertir el codigo del SPE en un archivo objeto y exportarlo como la
variable global ‘calculate_distance_handle’
embedspu calculate_distance_handle spe_distance spe_distance_csf.o
#Compilar el programa para el PPE enlazandolo con el objeto generado
en el paso anterior
gcc ppe_distance.c spe_distance_csf.o -lspe -o distance
#Ejecutar
./distance
De lo anterior caben destacar varias cosas:
La función main() de un SPE recibe el identificador del spe (que seria
similar al rank en MPI, una dirección efectiva, que generalmente será un buffer
de datos en el PPE, y una variable de entorno que no usamos.
El SPE primero realiza una transferencia DMA para obtener los datos
desde la dirección efectiva apuntada por el PPE y después de procesar los datos
realiza otra en sentido contrario para devolver los resultados.
El SPE devuelve un código, al igual que cualquier programa.
Las transferencias DMA se comportan como las funciones MPI_Send()
y MPI_Recv(), implementando también las tags, con la excepción de que el SPE
puede recibir datos de otro procesador sin que necesariamente éste se los haya
enviado, sino que mediante las direcciones efectivas (globales) puede tener
acceso a todo el mapa de memoria y obtenerlos por su cuenta, y sobretodo, no
notifica a una de las partes (la que no ha solicitado la transferencia) de que se ha
completado la misma.
Simulación del Cell
IBM proporciona también una completa suite que modela el comportamiento de
todos los elementos internos del Cell, de forma que se puede ejecutar código
virtualmente y analizar su rendimiento sin necesidad de disponer de una máquina
equipada con Cell.
20
Tiempos de comunicación en Cell
Resulta interesante medir el tiempo de comunicación entre núcleos, dado que no
existe un método exacto debido a las restricciones que impone el árbitro del bus.
Los tiempos de comunicación entre procesadores obtenidos en la máquina slabdi
del departamento de informática, empleando el interfaz MPI se muestran en la siguiente
gráfica:
3
2,5
2
N=
19999999
1,5
N=
59999999
N=
109999999
1
0,5
0
0a1
0a2
1a2
Donde se observaba un menor tiempo de comunicación entre los procesadores 0
y 2, que están instalados en la misma tarjeta.
En primer lugar hay que destacar que el sistema operativo bajo Cell, sólo ve 2
procesadores, el doble núcleo del PPE.
21
Con lo que la medida de tiempos en MPI no tiene sentido, ya que esta librería
todavía no implementa la compatibilidad con los SPE. Siendo la forma más eficiente de
implementar aplicaciones paralelas, el uso de las librerías y la API proporcionadas por
IBM (libspe, …..)
Una prueba sencilla de comunicación entre el PPE y un SPE, en milisegundos
dio una media de 2286 us, pero estas medidas se han realizado usando llamadas al
sistema operativo, con lo que no resulta eficiente, para ello sería necesario utilizar los
contadores internos de los SPE, que no están muy bien documentados. Para tener una
referencia fiable de los tiempos de comunicación entre SPEs, en el website de IBM se
muestra la siguiente tabla (medida en Gb/s).
Test configuration
Aggregate EIB BW 3.2 GHz
SPE1 <-> SPE3, SPE5 <-> SPE7, SPE0 <-> SPE2, SPE4 <-> SPE6
186 GB/s
SPE0 <-> SPE4, SPE1 <-> SPE5, SPE2 <-> SPE6, SPE3 <-> SPE7
197 GB/s
SPE0 <-> SPE1, SPE2 <-> SPE3, SPE4 <-> SPE5, SPE6 <-> SPE7
197 GB/s
SPE0 <-> SPE3, SPE1 <-> SPE2, SPE4 <-> SPE7, SPE5 <-> SPE6
197 GB/s
SPE0 <-> SPE7, SPE1 <-> SPE6, SPE2 <-> SPE5, SPE3 <-> SPE4
78 GB/s
SPE0 <-> SPE5, SPE1 <-> SPE4, SPE2 <-> SPE7, SPE3 <-> SPE6
95 GB/s
SPE0 <-> SPE6, SPE1 <-> SPE7, SPE2 <-> SPE4, SPE3 <-> SPE5
197 GB/s
22
Las fórmulas tradicionales para modelar el tiempo de comunicación en un anillo
no modelan muy bien la comunicación del EIB por las razones anteriores, y porque
además es un anillo bidireccional. Si no se tuviese en cuenta al árbitro de bus, para que
los datos que están en un nodo del SPE lleguen a todos los demás (difusión) bastaría
con que el anillo diese media vuelta, con lo que por un anillo los datos llegarían a la
mitad derecha y por el anillo que circula en sentido opuesto a la mitad izquierda, siendo
el tiempo equivalente a:
- Difusión:
o Techo(p-1/2)*(ts+m*tw)
Implementacion del conjunto de mandelbrot en Cell
Con esto pretendo implementar el conjunto de mandelbrot usando la arquitectura
Cell, y obtener una velocidad suficiente como para poder mostrar los conjuntos a
medida que se van generando, ya no a velocidad de vídeo (25 fps), pero sí a 1 o 2 fps.
La aplicación está basada en la práctica 3, y utiliza SDL (frame buffer) y la API
del Cell.
Utilizo el modelo de programación cola de tareas, de forma que el PPE va
asignando a los SPEs que van quedando libres la distintas subtareas.
Una forma de repartir el trabajo podría haber sido dividir la pantalla en 6 franjas
horizontales y asignar a cada SPE una franja, pero al estar los píxeles contiguos en
memoria no habría resultado en un buen uso de la memoria caché.
La forma de reparto dinámico consiste en que cada SPE calcula 16 pixels (dado
que su puerto de e/s es de 16 bytes, y cada píxel ocupa un byte), y se va avanzando de
arriba abajo y de izquierda a derecha en saltos de 16 pixels.
Para la comunicación con los SPE utilizo DMA y mailboxes.
Cada SPE tiene reservada en la memoria del PPE una estructura que contiene:
Id del procesador
Puntero a la estructura de trabajo
Puntero al Frame Buffer
Además de datos para rellenar la estructura y hacerla múltiplo de 16
bytes.
Además cada SPE tiene su estructura de trabajo, que contendrá los datos de su
trabajo actual (fila y columna a partir de la cual debe dibujar 16 pixels), coordenadas x e
y de mandelbrot, y el parámetro ex, para poder incrementar la columna y modificar el
parámetro x.
El bucle principal termina cuando se pulsa la tecla escape. Con los cursores se
pueden variar los parámetros ex, ey, ox y oy.
El flujo principal del PPE es el siguiente:
Cargar el código generado para los SPEs en los 6 disponibles.
Inicializar las estructuras de los SPE y enviar a todos los SPE el mensaje
ACTUALIZAR_INICIAL para que soliciten una copia de esa estructura
por DMA.
Bucle principal
Inicializar variables de mandelbrot.
Realizar el reparto inicial
23
Asignar a cada SPE un subproblema
Enviar el mensaje DATOS_NUEVOS a cada SPE
para que soliciten por DMA una copia de su estructura de
trabajo.
Mientras queden subproblemas
Esperar a recibir el mensaje SOLICITUD proveniente de
un SPE que ha terminado su trabajo y solicita más datos.
Actualizar los datos de la estructura de trabajo del
solicitante con datos nuevos y enviar el mensaje
DATOS_NUEVOS para que la recoja.
Realizar los incrementos apropiados de 16 en 16 pixels.
Si se pulsa la tecla escape
Enviar a los SPE el mensaje TERMINAR
Esperar a que acaben todos
Terminar aplicación
El flujo principal del SPE es el siguiente:
Bucle infinito
Recibir mensaje del PPE
Si es TERMINAR, salir del bucle infinito
Si es ACTUALIZAR_INICIAL solicito por DMA la
actualizacion de mi estructura de datos iniciales
Si es DATOS_NUEVOS:
Solicito por DMA la transferencia de mi estructura
de trabajo con datos nuevos (fila, columna, x e y).
Calculo 16 pixels partiendo de esas coordenadas
Envio los resultados a las coordenadas correctas
del frame buffer.
Envio al PPE el mensaje SOLICITUD para que me
proporcionen datos nuevos.
De esta forma se consigue generar un conjunto de mandelbrot de 640x480 en
aproximadamente 1 segundo.
Se pueden efectuar muchas optimizaciones sobre el código:
Vectorizar el código: El código que calcula un píxel es escalar, con
numerosos saltos, de forma que se ganaría mucho en rendimiento si se
utilizasen operaciones vectoriales.
Solapar computaciones y transferencias: Se podrían implementar colas
en los SPE para la recepción de datos y mensajes.
Utilizar doble buffering: para fomentar lo anterior y maximizar el
solapamiento.
Utilizar también el PPE para cálculos en lugar de sólo gestionar las
tareas de los SPE.
Desenrollar bucles y usar otras técnicas de optimización.
En la siguiente ilustración se muestra el resultado del programa, a 1 FPS, lo cual para
ser código escalar sin más optimizaciones es un buen resultado.
24
Se han publicado además resultados que demuestran que el Cell puede generar
conjuntos de mandelbrot de 640x480 pixels a una velocidad de 30 veces por segundo,
utilizando todo lo anterior y bajando a nivel de ensamblador.
Obteniendo rendimiento del Cell
Para obtener rendimiento hay que explotar el paralelismo tanto a nivel de
instrucción sectorizando el código, como a nivel de procesador haciendo una buena
gestión del reparto, proceso y comunicaciones.
A nivel de SPU, al ser un procesador vectorial se debe vectorizar el código para
aprovechar las instrucciones SIMD de la máquina, IBM propociona en sus librerías las
“SPU Intrinsics”, que son definiciones de alto nivel de instrucciones en ensamblador,
que permiten al programador hacer un uso más eficiente de la máquina con menos
esfuerzo y al compilador generar código más eficiente. Existe el tipo vector, que es un
registro de 128 bits de la máquina, el cual puede contener 16 valores de 8 bits, 8 valores
de 16 bits, 4 valores de 32 bits, o 2 valores de 64. Se declaran así:
vector unsigned short vec;
Lo que declara un vector de 8 valores de 16 bits. A continuación se puede operar
con el mediante instrucciones como:
spu_add(vec1, vec2) : Suma dos vectores
25
spu_sub(vec1, vec2) : Resta dos vectores
spu_mul(vec1, vec2) : Divide dos vectores
spu_insert((unsigned short)2, vec, 0) : Devuelve un vector
identico a vec, pero con 2 en su primera posición.
Existen también otras instrucciones para operaciones bit a bit, y para otros tipos
de cómputo.
Para evitar la penalización por saltos en los bucles, lo cual es importante dado
que no hay unidad de predicción de saltos y se asume siempre que el salto se toma, se
utilizan instrucciones que permiten escribir en un vector destino los valores de un vector
origen según los bits de otro vector máscara. De esta forma, estructuras como:
If(condicion)
A = x;
else
A = y;
Se pueden reescribir como:
Calcular x;
Calcular y;
A = Spu_sel(condicion, x, y);
También existen instrucciones para generar predoctores de saltos por
software, por ejemplo:
While (contador > 0)
{
contador --;
}
Se pueden reescribir como:
While(_builtin_expect(contador > 0, 1))
{
contador --;
}
Que significa que se genere una predicción de saltos para contador > 0,
teniendo en cuenta que la condición sólo se va a dar 1 vez.
Por otro lado, pueden y deben aplicarse otras técnicas de aceleración
tradicionales como el desenrollamiento de bucles (desenrollando decenas de
iteraciones, dado el elevado número de registros de la máquina), la planificación
de instrucciones para facilitar el solapamiento de operaciones, o la segmentación
software para mantener ocupadas todas las etapas del procesador.
Precauciones
Hay que tener en cuenta muchos factores de hardware a la hora de programar
aplicaciones de alto rendimiento en cell, las más importantes son:
-
Los SPE solo tienen un puerto de acceso a su local store, y el local store
contiene instrucciones y datos, con lo que si algún otro procesador está
26
-
-
-
-
accediendo intensivamente a su local store, y él mismo está realizando
operaciones sobre él, puede quedar detenido por inanición, ya que no habría
ancho de banda suficiente para leer instrucciones.
Se debe evitar todo lo posible realizar llamadas al sistema desde un SPE,
puesto que el sistema operativo lo está ejecutando el PPE, y una llamada al
sistema implica dos comunicaciones por el EIB.
Se deben transmitir por DMA datos múltiplo de 16 bytes siempre, y estos
datos deben estar alineados con una frontera de 16 bytes ( __attribute__
aligned(16) ), porque de lo contrario se producen errores de bus.
Los PPE pueden sobrescribir la cola del mailbox de entrada de un SPE,
produciéndose una pérdida de datos, el PPE no se bloquearía ante un intento
de escritura en un mailbox lleno, no como los SPEs, con lo que antes de
realizar una escritura se debe comprobar la disponibilidad del buzón.
Los SPE tienen 2 cauces segmentados, cada uno de los cuales implementa un
subconjunto de las instrucciones de la máquina. Los cauces se llaman par e
impar, y se aprovechan mejor si las instrucciones tienen direcciones de
memoria par e imar y solicitan el tipo de operación que implementa cada
cauce. El compilador inserta nops para explotar esto.
Multicomputación con Cell
Clusters con Playstation 3
En la Universidad de Carolina del Norte, el Dr. Frank Mueller pensó en usar la
potencia de la nueva PS3 para crear un sistema de computación de alto rendimiento por
una fracción del coste de los supercomputadores actuales del mercado. Está compuesto
de 8 máquinas (el primer clúster académico de estas características en el mundo), tiene
la potencia de un pequeño supercomputador por un coste total de 5000 dólares.
Una de las limitaciones existentes es la reducción de la capacidad de la memoria
RAM a la mitad (256 Mb) cuando la máquina opera en modo Linux, y algunas otras
restricciones que afectan ligeramente al rendimiento de red y de disco. Además existe
una limitación en la velocidad de los cálculos en doble precisión requeridos para
cálculos científicos, pero IBM ha anunciado que solucionará este problema en las
siguientes generaciones del procesador Cell.
El top 500 está encabezado por el BlueGene, con más de 130.000 procesadores
en el Laboratorio Nacional Lawrence Livermore. Según afirma el Dr. Mueller, su
cluster de PS3 no entra en el top 500 pero estima que con aproximadamente 10.000
máquinas PS3, cualquiera podría crear el supercomputador más rápido del mundo.
27
Algo similar se ha hecho en el Departamento de Astrofísica de la Universidad de
Massachussets, cuando el profesor Gaurav Khanna inició su proyecto de investigación
sobre las ondas gravitacionales se encontró con el problema del elevado coste de
alquilar tiempo de proceso en un supercomputador (5000 dólares), así que optó por
construir por mucho menos de lo que costaba el alquiler del supercomputador (3200
dólares), un cluster de 8 PS3, en el que ya se ha estado ejecutando durante un mes su
código de simulación (totalmente reescrito para Cell). Primero contactó con Sony
Computer Entertainment para que le prestasen una máquina, una vez realizadas sus
pruebas adquirió las 8. Según sus cálculos, este cluster de consolas tiene un rendimiento
equivalente al de 200 procesadores tradicionales de los que suelen emplearse en
supercomputadores. En ambos proyectos las conexiones entre nodos se realizan
mediante gigabit ethernet.
28
Lamentablemente todavía no se han publicado resultados concretos del
rendimiento de estos clusters.
En la UPC de Barcelona, el Mare Nostrum II está en proceso de planificación.
La máquina, además, supone en sí misma un proyecto científico. En tres años, si
los trabajos del grupo de Jesús Labarta de la UPC dan los resultados esperados,
habrá un Mare Nostrum II, un nuevo supercomputador que esperan construir junto
con IBM y que funcionará 25 veces más rápido, con un consumo de energía menor
y ocupando el mismo espacio que el actual inquilino de la capilla cibernética.
De momento, IBM les ha prestado a los científicos del Barcelona Supercomputing
Center varios chips Cell, que utilizarán para investigar la viabilidad de construir con
este tipo de arquitectura el nuevo supercomputador.
TerraSoft Solutions, la empresa responsable del desarrollo de YellowDog Linux
para PS3, comercializa clusters de PS3 de 8 y 32 nodos con su sistema operativo.
Conclusiones
Cell se basa en tres pilares fundamentales:
Una memoria de muy alta velocidad.
Unos tiempos de comunicación muy reducidos.
8 coprocesadores superesecalares + 1 maestro funcionando todos a la
misma frecuencia.
El código desarrollado para otras plataformas y simplemente recompilado para
Cell experimentará una bajada de rendimiento debido, entre otras cosas, a no utilizar los
SPEs y a la ausencia de la ejecución fuera de orden tanto del PPE cono de los SPE.
El código multi-hilo desarrollado para otras plataformas y recompilado para Cell
experimentará una bajada de rendimiento debido a lo anterior, y a que el PPE sólo
puede ejecutar dos hilos a la vez.
El código desarrollado específicamente para Cell alcanzará un rendimiento
espectacular comparado con otras arquitecturas convencionales, pero sufrirá de una
portabilidad prácticamente nula.
29
Los desarrolladores, sobretodo de juegos en el ámbito de la Playstation 3 no han
trabajado nunca con este hardware, y las herramientas de desarrollo que existen en la
actualidad (en especial compiladores) no están todavía muy maduras, con lo cual hay
que destacar que Sony ha hecho una apuesta muy arriesgada lanzando al mercado una
máquina que tal vez se adelanta a su tiempo.
El compilador es una de las claves para obtener el máximo rendimiento del Cell,
por citar un ejemplo al no haber ejecución fuera de orden es muy importante el orden en
que el compilador coloca las instrucciones.
Otro factor clave para obtener el máximo rendimiento en esta arquitectura
paralela de última generación, es como siempre conocer al detalle la arquitectura interna
del procesador y bajar a nivel de ensamblador cuando sea necesario, cosa que irá siendo
más factible a medida que se vayan formando nuevos expertos en esta arquitectura.
Bibliografía
•
•
•
•
•
Procesador Cell – Wikipedia
Cell Broadband Engine Architecture and its first implementation, A performance
view – IBM
Programming high-performance applications on the Cell BE processor, An
introduction to Linux on the PLAYSTATION 3 – IBM
Documentación de librerías del Cell – IBM
IBM Cell Workshop (www,power.org)
30