Curso Avanzado Cell Broad Engine by Acid-burn

Programación de procesadores multinúcleo.
Introducción a la programación del Cell Broadband Engine (Cell BE)
Programación del Cell BE:
-Introducción
-Arquitectura del Cell BE
-Programación del Cell BE
-Conclusiones

1-Introducción

Motivación
• Procesadores monolíticos --------------------------------------------------• Procesadores modulares
• Procesadores mononúcleo -------------------------------------------------• Procesadores multinúcleo
• Complejidad: diseño hardware ----------------------------------------------• Complejidad: desarrollo y optimización del software

Imagen


Desafíos
• Restricciones tecnológicas
• Los núcleos de procesamiento son más simples
– Menos transistores dedicados a lógica de control y a almacenamiento
– Falta de predicción de saltos y otras técnicas de especulación agresivas
– Cantidad limitada de memoria dentro del chip para cada núcleo (el tamaño de la memoria escala a un ritmo menor que el tamaño de los núcleos)
• Cantidad limitada de ancho de banda de acceso a memoria
• Restricciones software
• Complejidad que supone el desarrollo y optimización de nuevas aplicaciones paralelas
• Dificultad para construir nuevos compiladores capaces de extraer automáticamente el paralelismo sin intervención del programador
• Fracción del rendimiento pico que se puede conseguir con aplicaciones reales
• Migración de software existente construido sobre MPI, OpenMP, Cray Shmem, etc.

Objetivos
• Existe una arquitectura multinúcleo que está recibiendo una enorme atención debido a su tremendo potencial: Cell BE
• El Cell BE posee un modelo de programación no tradicional
• En esta sesión vamos a explicar los conceptos básicos en los que se basa este modelo de programación
• Para ello, resulta imprescindible conocer su arquitectura
• A continuación, pasaremos a analizar y ejecutar algunos ejemplos reales sobre una PS3
• Por último, esbozaremos las técnicas de optimización más relevantes


Cell BE
• Desarrollado conjuntamente por Sony (PS3), Toshiba e IBM (STI)
• Procesadormultinúcleo heterogéneo diseñado específicamente para explotar tanto el paralelismo de datos (SIMD) como el paralelismo a nivel de thread (Thread-Level Parallelism, TLP)
• 1 x Power Processor Element (PPE)
– Procesador de propósito general para ejecutar el SO y coordinar las tareas de los demás núcleos (el jefe)
• 8 x Synergistic Processing Element (SPE)
– Procesadores de propósito específico diseñados para tratar grandes cantidades de datos (los subordinados)
• Rendimiento pico de 204.8 Gflops (simple precisión) y 14.64 Gflops (doble precisión)


Arquitectura del Cell BE

Imagen



PPE

• Procesadormultinúcleo hetérogéneo
• 1 x Power Processor Element (PPE)
– 64-bit Power-architecture-compliant processor
– Dual-issue, in-order execution, 2-way SMT processor
– PowerPC Processor Unit (PPU)
– 32 KB L1 IC, 32 KB L1 DC, VMX unit
– PowerPC Processor Storage Subsystem(PPSS)
– 512 KB L2 Cache
– Procesador de propósito general para ejecutar el SO y el código de control
– Coordina las tareas realizadas por los demás núcleos

Imagen


SPEs
• Procesadormultinúcleo hetérogéneo
• 8 x Synergistic Processing Element (SPE)
– Dual-issue, in-order execution, 128-bit SIMD processors
– Synergistic Processor Unit (SPU)
– ISA SIMD (cuatro granularidades diferentes) con banco de registros SIMD (128 registros de 128 bits )
– 256 KB Local Storage (LS) para código/datos
– Memory Flow Controller (MFC)
– Memory-mapped I/O registers (MMIO Registers)
– DMA Controller: comandos para transferir datos desde/hacia memoria principal/LSs
– Procesadores de propósito específico diseñados para tratar datos
– Proporcionan la capacidad de cómputo principal

Imagen


Esquema general
• Element Interconnect Bus (EIB)
– Interconecta PPE, SPEs, y los controladores de memoria y E/S
– 4 anillos de 16 Bytes de ancho (2 en sentido horario y 2 en sentido antihorario)
– Hasta tres transferencias de datos simultáneas por anillo
– Algoritmo de ruta más corta
• Memory Interface Controller (MIC)
– 2 canales de memoria Rambus XDR (accesos en cada canal de 1-8, 16, 32, 64 o 128 Bytes)
• Cell BE Interface (BEI)
– 2 canales Rambus FlexIO I/O (1 de ellos puede unir 2 Cell BEs – 16 SPEs)

Imagen

El encanto del Cell BE
• Capacidad de procesamiento (3.2 GHz)
• Rendimiento pico de 204.8 Gflops (simple precisión) y 14.64 Gflops (doble precisión)
• Ancho de banda interno
• Rendimiento pico de 204.8 GB/s
• Ancho de banda de acceso a memoria (25.6 GB/s) y a los dispositivos de E/S (25 GB/s entrantes y 35 GB/s salientes)
• Permite que se realicen muchas peticiones de acceso a memoria de manera simultánea
• Hasta 128 operaciones de transferencia de DMA pendientes


Sistemas comerciales


• Cell BE disponible en:
• Play Station 3
– Alternativa más barata pero…
– …sólo 6 SPEs disponibles y < 200 MB para apps.
• IBM Blade Center QS20/21/22
• Mercury dual Cell-based blade
• Mercury Cell-based PCI Express board

Imagen Imagen Imagen

Supercomputadores
• IBM Roadrunner: supercomputador más rápido del mundo
– Hardware: diseño híbrido
– TriBlade: 2 x dual-core Opterons (16 GB RAM) + 4 x PowerXCell 8i (16 GB RAM)
– 12960 IBM PowerXCell 8i (Cell BE)
– 6480 AMD Opterons
– Software: basado en Linux
– Red Hat Enterprise Linux
– xCAT
– DaCS for Hybrid
– ALF
– Open MPI
Imagen

Programación del Cell BE

Filosofía general

• El Cell BE proporciona
• Gran capacidad de procesamiento (pico) dentro del chip (SPEs)
• Gran ancho de banda agregado (pico) dentro del chip (EIB)
• Pequeñas memorias locales dentro del chip (escasa reutilización de los datos)
• …lo que significa que deberíamos…
• Implementar algoritmos de distribución y balanceo de la carga para alimentar a todos los SPEs de manera equitativa
• Favorecer diseños algorítmicos que transfieran los datos dentro del chip en vez de acceder a memoria principal continuamente
• Incorporar esquemas algorítmicos (por ejemplo, double-buffering) que permitan solapar cómputo y comunicaciones
• Cuestiones críticas: sincronización, balanceo de la carga y movimiento de datos

Esquema general

• Los SPEs ejecutan threads creados por el PPE y sólo pueden acceder a código/datos dentro de su propio LS
• Programas diferentes escritos en C/C++ para el PPE y los SPEs
• Normalmente los SPEs ejecutan el mismo código pero sobre diferentes datos (Single ProgramMultiple Data, SPMD)
• Data Parallelism
• No obstante, también son posibles otros esquemas alternativos
• Task parallelism, pipeline, function offload, device management, etc.
• SDK proporcionado por IBM (usaremos la versión 3.0)
• Programas diferentes escritos en C/C++ para el PPE y los SPEs
• Código del PPE
– Tipos de datos vectoriales e intrinsics para utilizar la unidad VMX (por ejemplo, vector float or vec_add)
– Funciones de librería para manejar los threads y llevar a cabo tareas de comunicación y sincronización (por ejemplo, spe_context_run, spe_mfcio_put, spe_in_mbox_write)
• Código de los SPEs
– Tipos de datos vectoriales e intrinsics para hacer uso del ISA SIMD (por ejemplo, vector float or spu_add)
– Funciones de librería para llevar a cabo tareas de comunicación y sincronización (por ejemplo, mfc_get, spu_read_in_mbox)
• Librerías BLAS, LAPACK y SIMD Math

Otras librerías/entornos para programar el Cell BE

• Data Communications and Synchronization (DaCS) library y Accelerated Library Framework (ALF) se incluyen en la última versión del SDK de IBM
• Cell Superscalar (CellS) desarrollado por Barcelona Supercomputing Center
• Multicore Plus SDK Software desarrollado porMercury Computing Systems Inc.
• RapidMindMulticore Development Platform para procesadores multinúcleo de la familia x86 de AMD e Intel, GPUs de ATI/AMD y NVIDIA GPUs y Cell BE

Ejemplo 1: ¡HolaMundo!


EJEMPLO 1: ¡Hola Mundo!

Imagen

Ejemplo 1: ¡HolaMundo!

• Conectar PS3 (PuTTY/SSH)
• Usuario: AcidXX
• Clave: AcidXX.09
• Donde XX Є [01..30]
• [AcidXX@...]$

Imagen


• Descargar ejemplos.tgz de:

-Servidor Ftp: clustercell.no-ip.org
-Usuario: dark-alex.org
-Pass: dark-alex.org
• Extraer ficheros de ejemplo y acceder al directorio de ejemplo /cellbe_hello

Imagen

• Compilar:
• [AcidXX@...] $ cat ../ps3.env export CELL_TOP=/opt/ibm/cell-sdk/prototype
• [AcidXX@...] $ source ../ps3.env
• [AcidXX@...] $ make
• Ejecutar:
• [AcidXX@...] $ ppu/cellbe_hello 1
• Editar código:
• [AcidXX@...] $ vim ppu/cellbe_hello_ppu.c
• [AcidXX@...] $ vim spu/cellbe_hello_spu.c

• Código PPE
int main(int argc, char **argv)
{
int num_spes, i;
if ( argc < 2) {
fprintf(stderr, "Usage: %s NUM_SPEs\n", argv[0]); exit(1);
}
/* set number of SPEs */
num_spes = atoi(argv[1]);
/* figure out the number of availabe SPEs */
if ((spe_cpu_info_get(SPE_COUNT_USABLE_SPES, -1)) < num_spes) {
fprintf(stderr, "System doesn't have enough working SPEs.\n"); return -1;
}

/* marshall thread_info structures */
for(i=0; i < num_spes; i++)
{
/* create the SPE context */
if ((tinfo[i].ctx = spe_context_create(SPE_MAP_PS, NULL)) == NULL) {
perror("spe_context_create"); exit (1);
}
/* load the SPE program into the SPE context */
if (spe_program_load(tinfo[i].ctx, &cellbe_hola_spu) != 0) {
perror("spe_program_load\n"); exit (1);
}
/* set SPE thread ID */
tinfo[i].id = i;
}

/* run the SPE threads */
for(i=0; i < num_spes; i++)
{
/* spawn Linux threads to run SPE contexts */
if (pthread_create (&(tinfo[i].thread), NULL,
&thread_function, &tinfo[i])){
perror("pthread_create");
exit (1);
}
printf("PPE: thread for SPE %d created\n", i);
}
/* Computation is distributed among SPEs */

for(i=0;i<num_spes;i++)
{
/* wait for Linux threads */
if (pthread_join (tinfo[i].thread, NULL)) {
perror("Failed pthread_join");
exit (1);
}
/* destroy the SPE context */
if (spe_context_destroy(tinfo[i].ctx) != 0) {
perror("spe_context_destroy");
exit (1);
}
...
}

for(i=0;i<num_spes;i++)
{
...
/* check the SPE exit status */
if (tinfo[i].stop_info.stop_reason == SPE_EXIT) {
if (tinfo[i].stop_info.result.spe_exit_code != 0) {
fprintf(stderr, "SPE %d returned a non-zero exit status.\n", i); exit(1);
}
} else {
fprintf(stderr, "SPE %d abnormally terminated.\n", i); exit(1);
}
}
printf("*** Test completed successfully ***\n");
return 0;
}

/* function executed by each Linux thread */
void *thread_function(void *arg)
{
unsigned int entry = SPE_DEFAULT_ENTRY;
thread_info *tinfo = (thread_info *) arg;
if ((spe_context_run(tinfo->ctx, &entry, 0, (void *) tinfo->id, 0, &tinfo->stop_info))<0)
{
perror("spe_context_run");
exit (1);
}
pthread_exit(NULL);
}

• Código SPEs

int main(uint64_t speid, uint64_t id)
{
/* ¡Hola mundo! */
fprintf(stderr, "SPE %lld (%llx): ¡Hola mundo!\n", id, speid);
return 0;
}


Comunicación y sincronización

• El PPE y los SPEs se comunican y sincronizan utilizando un conjunto de mecanismos soportados por el hardware:
• Buzones (mailboxes), señales (signals), operaciones atómicas (atomic operations) y transferencias de DMA
• Los registros MMIO que corresponden a buzones, señales y DMA controller de cada SPE están, como su nombre indica, mapeados en memoria.
• El PPE y los SPEs pueden programar el DMA controller para realizar transferencias entre la memoria principal, los LSs y los dispositivos de E/S
• Transferencias de DMA iniciadas por un SPE o por el PPE, bloqueantes o no bloqueantes, entre memoria principal y el LS de un SPE o entre dos LSs de dos SPEs diferentes
• Restricciones en las direcciones de memoria y el tamaño
• Las direcciones de memoria de origen y destino deben estar alineadas con un límite de 16 B
• Tamaño de DMA de 1, 2, 4, 8B o múltiplos de 16B hasta un máximo de 16 KB
• NOTA: se obtiene un mejor rendimiento cuando las direcciones de origen y destino están alineadas con un límite de 128 Bytes y el tamaño de la trasferencia es un múltiplo de 128 Bytes
• Buzones (mailboxes): permiten el intercambio de mensajes de 32 bits entre los SPEs y el PPE
• Envío de mensajes desde el SPE
– 1-entry SPU Write Outbound Mailbox
– 1-entry SPU Write Outbound Interrupt Mailbox
• Recepción de mensajes en el SPE
– 4-entry SPU Read Inbound Mailbox
• Cada buzón tiene un canal y su correspondiente registro MMIO
• El canal permite al SPE propietario leer/escribir de/en los buzones
• El registro MMIO permite a otros SPEs y al PPE leer/escribir de/en los buzones

Ejemplo 2: Uso de buzones


EJEMPLO 2: Uso de buzones

Ejemplo 2: Uso de buzones

• Acceder a directorio de ejemplo:
• [AcidXX@...] $ cd Acid/cellbe_mbox
• Compilar:
• [AcidXX@...] $ source ../ps3.env
• [AcidXX@...] $ make
• Ejecutar:
• [AcidXX@...] $ ppu/cellbe_mbox 1
• Editar código:
• [AcidXX@...] $ vim ppu/cellbe_mbox_ppu.c
• [AcidXX@...] $ vim spu/cellbe_mbox_spu.c


• Código PPE


int main(int argc, char **argv)
{
...
/* marshall the control block and the thread_info structures */
for(i=0; i < num_spes; i++)
{
...
/* get memory-mapped address of SPEs' control area (to use mailboxes) */
tinfo[i].control_addr = (void *) spe_ps_area_get(tinfo[i].ctx, SPE_CONTROL_AREA);
cb.control_addr[i] = (EA) tinfo[i].control_addr;
...
}
...
}


int main(int argc, char **argv)
{
...
in_mbox_data = MBOX_HELLO;
/* PPE -----MBOX-----> SPE (check if we can write in the inbound mailbox) */
while(spe_in_mbox_status(tinfo[0].ctx) == 0);
res = spe_in_mbox_write(tinfo[0].ctx, &in_mbox_data, 1,SPE_MBOX_ALL_BLOCKING);
assert(res == 1);
/* PPE <-----MBOX----- SPE (check if there is any message in the outbound mailbox) */
while(spe_out_mbox_status(tinfo[num_spes-1].ctx) == 0);
res = spe_out_mbox_read(tinfo[num_spes-1].ctx, &out_mbox_data, 1);
assert(res == 1); assert(out_mbox_data == MBOX_HELLO_ACK);
fprintf(stderr, "PPE: MBOX test sent %d and received %d\n", in_mbox_data, out_mbox_data);
...
}



• Código SPEs

int main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
/* DMA call to get the control block from main memory. */
mfc_get(&cb, cb_ptr, sizeof(control_block), CB_TAG, 0, 0);
/* Now, we set the "tag bit" which is always 1 left-shifted by the tag
specified with the DMA for whose completion you wish to wait. */
mfc_write_tag_mask(1<<CB_TAG);
/* Finally, issue the read and wait for DMA completion. */
mfc_read_tag_status_all();
fprintf(stderr, "SPE %lld (%llx): got cb (%p) from main memory (0x%llx)\n",
id, speid, &cb, cb_ptr);
...
}


int main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
in_mbox_data = spu_read_in_mbox(); assert(in_mbox_data == (MBOX_HELLO+id));
if (id < (cb.num_spes-1)) {
control_area.SPU_In_Mbox = in_mbox_data + 1;
CHECK_DMA_ALIGN(&control_area.SPU_In_Mbox, cb.control_addr[id+1] +
SHIFT_SPU_IN_MAILBOX, sizeof(unsigned int));
mfc_put(&control_area.SPU_In_Mbox, cb.control_addr[id+1] + SHIFT_SPU_IN_MAILBOX,
sizeof(unsigned int), MBOX_TAG, 0, 0);
mfc_write_tag_mask(1<<MBOX_TAG); mfc_read_tag_status_all();
fprintf(stderr, "SPE %lld (%llx): MBOX test: received %d sent %d\n",
id, speid, in_mbox_data, in_mbox_data + 1);
}


...
else if (id == (cb.num_spes-1)) {
spu_write_out_mbox(MBOX_HELLO_ACK);
fprintf(stderr, "SPE %lld (%llx): MBOX test: received %d sent %d\n",
id, speid, in_mbox_data, MBOX_HELLO_ACK);
}
...
}


Comunicación y sincronización: señales

• Señales (signals): permiten a los SPEs recibir notificaciones de 32 bits de otros SPEs o incluso del PPE
• Recepción de notificaciones en el SPE
– 1-entry SPU Signal Notification 1
– 1-entry SPU Signal Notification 2
• Cada registro de señales tiene un canal y su correspondiente registroMMIO
• El canal permite al SPE propietario leer el registro de señales que se resetea
• El registroMMIO permite a otros SPEs y al PPE escribir en el registro de señales en modo sobrescritura (destruye el valor anterior) o en modo OR (combina el valor nuevo y el valor anterior con un OR lógico a nivel de bits)


EJEMPLO 3: Uso de señales

Ejemplo 3: Uso de señales

• Acceder a directorio de ejemplo:
• [AcidXX@...] $ cd Acid/cellbe_signal
• Compilar:
• [AcidXX@...] $ source ../ps3.env
• [AcidXX@...] $ make
• Ejecutar:
• [AcidXX@...] $ ppu/cellbe_signal 1
• Editar código:
• [AcidXX@...] $ vim ppu/cellbe_signal_ppu.c
• [AcidXX@...] $ vim spu/cellbe_signal_spu.c

• Código PPE

int main(int argc, char **argv)
{
...
/* marshall the control block and the thread_info structures */
for(i=0; i < num_spes; i++)
{
...
/* get memory-mapped address of SPEs' signal area */
tinfo[i].sig1_addr = (void *) spe_ps_area_get(tinfo[i].ctx, SPE_SIG_NOTIFY_1_AREA);
cb.sig1_addr[i] = (EA) tinfo[i].sig1_addr;
...
}
...
}


int main(int argc, char **argv)
{
...
/* PPE -----SIGNAL-----> SPE */
in_signal = SIGNAL_HELLO;
res = spe_signal_write(tinfo[0].ctx, SPE_SIG_NOTIFY_REG_1, in_signal); assert(res == 0);
/* PPE <------MBOX------ SPE (check if there is any message in the outbound mailbox) */
while(spe_out_mbox_status(tinfo[num_spes-1].ctx) == 0);
res = spe_out_mbox_read(tinfo[num_spes-1].ctx, &out_mbox_data, 1); assert(res == 1);
assert(out_mbox_data == MBOX_HELLO_ACK);
fprintf(stderr, "PPE: SIGNAL test sent %x and received %d\n",
SIGNAL_HELLO, out_mbox_data);
...
}



• Código SPEs
int main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
/* DMA call to get the control block from main memory. */
mfc_get(&cb, cb_ptr, sizeof(control_block), 31, 0, 0);
/* Now, we set the "tag bit" which is always 1 left-shifted by the tag
specified with the DMA for whose completion you wish to wait. */
mfc_write_tag_mask(1<<31);
/* Finally, issue the read and wait for DMA completion. */
mfc_read_tag_status_all();
fprintf(stderr, "SPE %lld (%llx): got cb (%p) from main memory (0x%llx)\n",
id, speid, &cb, cb_ptr);
...


int main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
in_signal = spu_read_signal1();
if (id < (cb.num_spes-1)) {
spe_sig_notify_1_area.SPU_Sig_Notify_1 = in_signal + 1;
mfc_sndsig(&spe_sig_notify_1_area.SPU_Sig_Notify_1,
cb.sig1_addr[id+1] + SHIFT_SIG_NOTIFY_1,
SIGNAL_TAG, 0, 0);
mfc_write_tag_mask(1<<SIGNAL_TAG);
mfc_read_tag_status_all();
}
...
}

int main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
else if (id == (cb.num_spes-1)) {
spu_write_out_mbox(MBOX_HELLO_ACK);
}
fprintf(stderr, "SPE %lld (%llx): SIGNAL test: received %x and sent %x\n",
id, speid, in_signal, in_signal + 1);
...
}



Comunicación y sincronización: operaciones atómicas

• Operaciones atómicas (atomic operations): permiten ejecutar operaciones de lectura-modificación-escritura sobre números enteros almacenados en memoria principal de manera atómica
• Por ejemplo:
• t0: shared_var = 0; /* Memoria principal */
• t1/SPE0: my_var0 = atomic_add_return(3, shared_var,)
• t1/SPE1: my_var1 = atomic_add_return(5, shared_var)
• t2: shared_var = 8; /* Memoria principal */
• Dos escenario posibles:
• Escenario 1: my_var0 = 0 y my_var1 = 3
• Escenario 2: my_var0 = 5 y my_var1 = 0

EJEMPLO 4: Uso de operaciones atómicas


Ejemplo 4: Uso de operaciones atómicas

• Acceder a directorio de ejemplo:
• [AcidXX@...] $ cd Acid/cellbe_atopmicops
• Compilar:
• [AcidXX@...] $ source ../ps3.env
• [AcidXX@...] $ make
• Ejecutar:
• [AcidXX@...] $ ppu/cellbe_atomicops 1
• Editar código:
• [AcidXX@...] $ vim ppu/cellbe_atomicops_ppu.c
• [AcidXX@...] $ vim spu/cellbe_atomicops_spu.c

• Código PPE

int main(int argc, char **argv)
{
...
/* ATOMIC Ops test */
fprintf(stderr, "PPE: shared variable is %d\n", shared_var);
...
/* wait until all SPEs are done */
while( shared_var != (SHARED_VALUE+(num_spes * INC_VALUE)) );
fprintf(stderr, "PPE: shared variable is %d\n", shared_var);
...
}


• Código SPEs
int main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
/* atomically increment shared variable in main memory */
res = _atomic_add_return(INC_VALUE, cb.shared_var_ptr); /* fetch&add */
fprintf(stderr, "SPE %lld (%llx): ATOMIC OPS test: obtained %d\n", id, speid, res);
...
}


Optimización del código

• Alineamiento de buffers origen/destino de transferencias
• Asignación estática: __attribute__(aligned(16))
• Asignación dinámica: void *malloc_align(size_t size, unsigned int n)
• Uso de código SIMD para aprovechar completamente los SPEs
• Double-buffering para solapar las transferencias de DMA con el cómputo
• Reducción del impacto de los saltos
• Branch hinting
if ( __builtin_expect((a>b),0) ) c+=a else c+=b

• Loop unrolling
• Function inlining
• Reordenación de instrucciones para maximizar los ciclos de dual-issue
• Double-buffering

Imagen

EJEMPLO 5: Suma de dos vectores

Suma de dos vectores

• Acceder a directorio de ejemplo:
• [AcidXX@...] $ cd Acid/cellbe_vectorAdd
• Compilar:
• [AcidXX@...] $ source ../ps3.env
• [AcidXX@...] $ make
• Ejecutar:
• [AcidXX@...] $ ppu/cellbe_vectorAdd 1
• Editar código:
• [AcidXX@...] $ vim ppu/cellbe_vectorAdd_ppu.c
• [AcidXX@...] $ vim spu/cellbe_vectorAdd_spu

• Código PPE

int main(int argc, char **argv)
{
...
/* float source and destination vectors */
float vec1[MAX_VECTOR] __attribute__(aligned(128));
float vec2[MAX_VECTOR] __attribute__(aligned(128));
float vec_res[MAX_VECTOR] __attribute__(aligned(128));
...
/* init the control block */
cb.num_spes = num_spes = atoi(argv[1]);
cb.vec1_ptr = (EA) vec1;
cb.vec2_ptr = (EA) vec2;
cb.vec_res_ptr = (EA) vec_res;
...
}

int main(int argc, char **argv)
{
...
/* initialize vectors */
memset(vec1, sizeof(float) * MAX_VECTOR, 0.0);
for(i=0;i<MAX_VECTOR;i++) vec1[i] = DMA_VALUE1;
memset(vec2, sizeof(float) * MAX_VECTOR, 0.0);
for(i=0;i<MAX_VECTOR;i++) vec2[i] = DMA_VALUE2;
memset(vec_res, sizeof(float) * MAX_VECTOR, 0.0);
...
}


int main(int argc, char **argv)
{
...
/* tell SPEs they can proceed */
in_mbox_data = DMA_HELLO;
for(i=0;i<num_spes;i++) {
/* PPE -----MBOX-----> SPE (check if we can write in the inbound mailbox) */
while(spe_in_mbox_status(tinfo[i].ctx) == 0);
res = spe_in_mbox_write(tinfo[i].ctx,&in_mbox_data,1,SPE_MBOX_ALL_BLOCKING);
assert(res == 1);
}
...
}

int main(int argc, char **argv)
{
...
/* wait until all SPEs are done */
for(i=0;i<num_spes;i++) {
/* PPE <------MBOX------ SPE (check if there is any message in the outbound mailbox) */
while(spe_out_mbox_status(tinfo[i].ctx) == 0);
res = spe_out_mbox_read(tinfo[i].ctx, &out_mbox_data, 1);
assert(res == 1);
assert(out_mbox_data == DMA_HELLO_ACK);
}
...
}


• Código SPEs

int main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
in_mbox_data = spu_read_in_mbox();
assert(in_mbox_data == DMA_HELLO);
elems = MAX_VECTOR/cb.num_spes;
myid = (unsigned int) id;
mysize = elems * sizeof(float);
vec1_ptr = (int) cb.vec1_ptr + (int) myid * mysize;
vec2_ptr = (int) cb.vec2_ptr + (int) myid * mysize;
vec_res_ptr = (int) cb.vec_res_ptr + (int) myid * mysize;
...
}


int main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
/* copy vec1 and vec2 fragments from main memory */
mfc_get(vec1, vec1_ptr, mysize, DMA_TAG, 0, 0);
mfc_get(vec2, vec2_ptr, mysize, DMA_TAG, 0, 0);
mfc_write_tag_mask(1<<DMA_TAG);
mfc_read_tag_status_all();
...
}


int main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
/* add vec1 and vec2 fragments */
for(i=0;i<elems/4;i++) {
vec_res[i] = spu_add(vec1[i], vec2[i]);
}
...
}


i
nt main(uint64_t speid, uint64_t id, EA cb_ptr)
{
...
/* copy vec_res fragment back in main memory */
mfc_put(vec_res, vec_res_ptr, mysize, DMA_TAG, 0, 0);
mfc_write_tag_mask(1<<DMA_TAG);
mfc_read_tag_status_all();
...
}




Conclusiones

• El Cell BE es un procesador multinúcleo heterogéneo muy potente y versátil
• Su programación requiere conocer tanto su arquitectura como los mecanismos de comunicación/sincronización que posee
• Hemos analizado y ejecutado ejemplos sencillos de programación del Cell BE que hacen uso de las transferencias de DMA, los buzones, las señales y las operaciones atómicas
• La programación y optimización de código para el Cell BE es una tarea ardua y compleja aunque muy potente.

PD: Fuentes Ibm, Juan Peinador, apuntes varios...
pero si esto lo contaban al final de los capítulos de los diminutos!!!!

no en serio [tadoramo]
santo079 escribió:Ya lo sabia


¿? Reportado....

@Acid: un curro k te cagas, me lo estoy leyendo aunke no entiendo muxo, pero menuda currada.
No suelo postear pq no tengo nada k decir, xDD, pero mola k la gente kiera compartir y enseñar a los demas ;) asik desde aki enorabuena :)
todavía no esta terminado! tengo poco tiempo, lo terminaré en breve.
Uff... pero un tutorial para esto es pasarse de extensión ¿no? Un curro exagerado de bueno, ¿tocarás la parte de la programación concurrente?
PRiSMiWi escribió:Uff... pero un tutorial para esto es pasarse de extensión ¿no? Un curro exagerado de bueno, ¿tocarás la parte de la programación concurrente?

hola prismiwi para la programacion concurrente creo q hay manuales online de alguna compañia desarrolladora que los comparte, logicamente de forma gratuita. Si estais muy interesados en tecnicas de programación de paralelismo potencial entre tareas puedo buscar algo. Un saludo
Es que considero que es la pieza clave que le falta al tutorial. Recursos compartidos, semáforos, monitores, etc...

Porcierto, no te lo tomes a mal, es todo constructivo, el tuto tiene una pinta excelente. Yo ni loco me pondría a hacer uno, un saludo.
Enhorabuena por el tutorial

Impresionante [plas]
yo solo entiendo una cosa, cell es una pesadilla xD
yevon escribió:yo solo entiendo una cosa, cell es una pesadilla xD


Para los que quieren simplificar la programación de los SPE: hilo_cbexlc-xl-c-c-for-multicore-acceleration-v10-1_1216529
muchas gracias compañero. Entiendo lo justito de programación a niveles de usuario pero es una currada.

A seguir se ha dicho
MUCHAS GRACIAS x el el curre paisano!!!
Aunque pasé de la programación en su día cuando conocí las redes... te felicito, un curro impresionante el que te has dado. [oki]
Acid-burn,yo tengo una duda,resulta que he me descargado el CD del Cell SDK 3.0(no se si es el ultimo que salio),y me sale que las herramientas de compilacion para el Cell se pueden instalar tambien en procesadores x86(ademas de ppc64)¿eso quiere decir que podria compilar las herramientas del Cell en un procesador x86 y luego ejecutarlas en el Linux usando el Cell?
Un saludo.
(mensaje borrado)
CORAGON2 escribió:[offtopic]Acid-Burn que sepas que no eres bienvenido por un sitio... [poraki] [/offtopic]


Impresionante tu mensaje Coragon2, el sitio al q llamas es el demonio de hadas para q la gente lo sepa. Este señor no quiere que comparta mis conocimentos con el resto de webs. Si por eso no soy "bienvenido" como tu dices, pues vosotros mismos. La informacion la considero un derecho y como tal comparto mis trabajos y saberes al resto de foros.
Jur...

Por favor, volved al tema del hilo, gracias.

Un saludo.
Yo me remito a la pregunta que le hice a Acid-burn sobre las herramientas de compilacion del Cell si se pueden usar para compilar en los procesadores x86,por que en la PS3 con la poca RAM de la que dispone se hace eterno compilar algo en Linux.
Un saludo.
Psmaniaco escribió:Yo me remito a la pregunta que le hice a Acid-burn sobre las herramientas de compilacion del Cell si se pueden usar para compilar en los procesadores x86,por que en la PS3 con la poca RAM de la que dispone se hace eterno compilar algo en Linux.
Un saludo.

psmaniaco sep se puede compilar lo que tu quierias para posteriormente portarlo a ps3 ;)

Siento no responder más rápido pero apenas tengo tiempo. Un saludo! y haber si nos sorprendes con una aplicación específica para linux ps3. ;)
Gracias por la respuesta,espero poder hacer algo,desde luego estoy examinado las herramientas de compilacion para el Cell,JOPEEE es complicado de programar pero me gustan los riesgos de este tipo jejeje XD .
Aparte de esto llevo toda la noche peleandome con el firewall de Linux para conseguir abrir los puertos de Linux en la PS3 y me esta volviendo mico [sonrisa] ,asi que veremos cuando empiece a programar para el Cell,pero he de afirmar que soy un autentico novato en esto de programar para procesadores multinucleo.
Un saludo.
Mejorada la imagen de la estructura de Cell BE
Desde luego es un curro de la hostia... gracias!!
Lanik79 escribió:Desde luego es un curro de la hostia... gracias!!


Me alegro que os guste. En breve prepararé otros manuales sobre linux, diferencias entre ps3, xbox 360 y wii
Acid, eres un maquina... Me parece increible lo que has hecho, y te lo dice un posible programador futuro! Ojala consigas algo importante, mas que esto, en cuanto a scene (pocas cosas son mas importantes que ayudarnos a programar!).
Un saludo y muchisimas gracias, chincheta para el hilo ya!! =D
Gracias! haber si con un granito de arena cada uno conseguimos que la scene empiece de verdad! Un saludo
RadeR_8 está baneado por "Clon de usuario baneado"
Perdonad mi ignorancia pero esto para que sirve?
Esto sirve para programar las SPU que trae el Cell,para que me entiendas el Cell lleva un nucleo central(que es el procesador propiamente dicho)llamado PPU y que es de arquitectura Power PC(ppc,la arquitectura de la mayoria de los PCs que tenemos en casa es x86).Despues lleva 8 coprocesadores conectados a la PPU llamados SPU,pero solo se pueden usar 6 ya que uno viene desactivado de fabrica y el otro lo usa la PS3 para su uso;con lo que nos quedan 6 para poder programarlos con Linux,para ponerte un ejemplo el Linux en la PS3 la tarjeta grafica que el chip RSX viene bloquedado por Sony,por lo que no teniamos aceleracion 3D en Linux al principio,pero lo arreglaron usando una de las SPU para que haga la funcion de tarjeta aceleradora con lo que Linux ya tiene aceleracion 3D,este seria uno de los usos que se le podrian dar a las SPU del Cell.
Un saludo.
psmaniaco, te animas a hacer un engine opengl usando las SPU del cell? osando tiled-rendering creo que seria posible y sacariamos algun que otro fps de forma sencilla...

aqui tienes una aplicacion real hecha con el SDK de IBM para renderizar fractales en tiempo real usando el CELL. http://ozlabs.org/~jk/projects/lca2008-hackfest/

es MUY instructivo, porque te da un tutorial desde 0 de como se deben hacer y/o adaptar las cosas al cell.

mi idea es hacer un engine opengl basandome en MESA SOFTWARE RENDERING y renderizar en bloques de 16x16 u 8x8 pixeles, usando parte de los 256KBs de los SPU como texture-cache
Oye pues no me importaria,pero me tendriais que dar un cursillo XD ya que en programacion de Linux he perdido un poco de practica(hace años que compile mi ultimo kernel jejeje).
Por cierto ahora que lo mencionas f5inet,ya que las ultimas distros de linux usan una de las SPU como aceleradora 3D tengo una pequeña duda¿se podria programar otra SPU para la misma tarea?,es decir usar 2 para aceleracion grafica por hardware,creo que podrian funcionar en paralelo,aunque tengo mis dudas.
Un saludo.
Psmaniaco escribió:Oye pues no me importaria,pero me tendriais que dar un cursillo XD ya que en programacion de Linux he perdido un poco de practica(hace años que compile mi ultimo kernel jejeje).
Por cierto ahora que lo mencionas f5inet,ya que las ultimas distros de linux usan una de las SPU como aceleradora 3D tengo una pequeña duda¿se podria programar otra SPU para la misma tarea?,es decir usar 2 para aceleracion grafica por hardware,creo que podrian funcionar en paralelo,aunque tengo mis dudas.
Un saludo.


claro que puedes usar 2 SPU para renderizar, incluso las 6 disponibles, el problema es como repartes el trabajo.

actualmente, con el vistazo somero que le he echado a lo que estan haciendo, bajo mi modesto punto de vista, estan tomando el camino equivocado, o sea, estan descargando el hilo que se encarga de renderizar en 3D a una de las SPU en lugar del PPU, pero bloquean el hilo principal del PPU para que no se produzcan llamadas 'reentrantes', o sea, mientras el SPU calcula como un loco, le hilo que corre en el PPU esta parado/bloqueado. por supuesto esto es beneficioso, en el sentido que si ademas de calculos 3D tenemos que calcular fisicas y/o sonido, otros hilos se pueden encargar de ello, pero estamos 'malgastando' los SPU, porque estas poniendo una unica SPU, con 256KB de memoria local (los acceso al resto de la memoria se deben hacer previa peticion DMA de lectura, y su posterior DMA de escritura de los resultados) a renderizar toda una pantalla de 1920x1080 (suponemos FullHD)

Yo lo haria poniendo 2-4-6 SPU a renderizar bloques de 32x32 (32x32x4(ARGB)=4096=4KB + 32x32x4(Zbuffer float)=4096=4KB) y renderizaria tantos bloques de 32x32 como fueran necesarios hasta completar la resolucion de 1920x1080 (harian falta 2040 bloques de 32x32). en datos necesarios tan solo ocuparia 8KB de los 256KB de local-store de la SPU, el resto, lo dividiria en 64KB de geometry-cache, 64KB de texture-cache y los 120KB que me sobran para el codigo necesario.

lanzaria 4 hilos SPU para renderizar, dividiria la pantalla en 4 partes diferente: arriba (casi siempre cielo, con un hit-rate de texture-cache bastante majo y poca geometria), abajo (casi siempre suelo, quizas el ratio de texture-cache baje, pero el hit-rate de geometry-cache compensaria), y la parte central la dividira nuevamente en centro-izquierda y centro-derecha (cada uno en su hilo de SPU correspondiente). cada SPU renderizaria su region correspondiente (cogiendo los datos de geometria por DMA y escribiendo por DMA su porcion calculada al framebuffer) y si por un casual terminara su porcion antes que las demas, notificaria al PPU para que subdividiera la SPU que fuese mas atrasada para repartir nuevamente el renderizado al vuelo.

pero claro, no tengo demasiado tiempo para llevar eso a la practica, y me interesa mas hacer esa especie de 'tiled-rendering' para un raytracer que tengo en mente.
F5inet tengo montada una ftp en la que ire subiendo información que os puede interesar sobre cell.
Direccion del ftp: clustercell.no-ip.org
Usuario: elotrolado.net
Contraseña: eol
Puerto : 24
En la carpeta Documentos encontrarás información sobre cell y programacion para cell. Voy a ir metiendo cosas constantemente, asi que si os interesa ;)
Hola, gracias Acid-burn por las documentaciones, voy a ponerme a ver, si puedo hacer un proyecto que tengo en mente, pero usando los SPU ^^.
Buena explicacion f5inet,lo que no me termina de quedar claro es esto:
estan descargando el hilo que se encarga de renderizar en 3D a una de las SPU en lugar del PPU, pero bloquean el hilo principal del PPU para que no se produzcan llamadas 'reentrantes', o sea, mientras el SPU calcula como un loco, le hilo que corre en el PPU esta parado/bloqueado. por supuesto esto es beneficioso, en el sentido que si ademas de calculos 3D tenemos que calcular fisicas y/o sonido, otros hilos se pueden encargar de ello, pero estamos 'malgastando' los SPU, porque estas poniendo una unica SPU, con 256KB de memoria local (los acceso al resto de la memoria se deben hacer previa peticion DMA de lectura.
Un saludo.
¿que es lo que no entiendes? ¿lo de el hilo bloqueado en el PPU o los accesos DMA de la SPU a RAM?

a ver, en un juego normal, existe un unico hilo de ejecucion, y el algoritmo es tal que:
1)recojo eventos de teclado/raton
2)realizo comunicaciones de red
3)actualizo la logica del juego
4)calculo sonido
5)dibujo en pantalla
6)goto 1

si tienes un unico hilo de ejecucion, todo tu programa se bloqueara en 5 hasta que el SPU termine. si el programador es lo bastante inteligente como para paralelizar (mediante pthreads) el sonido con los graficos (practicamente lo unico paralelizable en el algoritmo superior) la PPU resultaria liberada para calcular el sonido mientras el SPU calcula los graficos. se pueden hacer un monton de optimizaciones mas, pero por mantener el ejemplo simple, no las vamos a abordar.

Con respecto al tema del DMA y los 256KB de los SPU: la PPU tiene acceso directo a toda la RAM, y puede acceder a toda ella sin problemas. si accede a la RAM, tiene una penalizacion (aproximada, no me he puesto a calcular al ciclo) de unos 30 ciclos. si el SPU quiere traerse/llevar algun dato de/a RAM, tiene que iniciar un DMA y ademas esperar que complete ese DMA, lo cual, aun en el mejor de los casos (DMA ocioso, RAM ociosa) nos iriamos a mas de 200 ciclos. por supuesto, una vez iniciada la transferencia DMA, nos traeriamos de golpe unos pocos de bytes, y los siguientes bytes llegarian aproximadamente 1 cada 5 ciclos. por eso, en algoritmos 'metralleta' (o sea, con muchos accesos cortos a RAM para leer texturas y para escribir resultados en un framebuffer), se usa alguna tecnica de cacheado en memoria local/embebida/incrustada (por eso las tarjetas graficas tienen su propia RAM, y no usan la principal de sistema, por eso la 360 tiene 10MB de eDRAM incrustada en la GPU, por eso los SPU tienen 256KB de local-store).
la estrategia logica para programar los SPU es (usando triple buffering):
1)programo DMA para traerme todo lo necesario para trabajar ahora (buffer1)
2)programo DMA para traerme todo lo necesario para trabajar luego (buffer2)
3)espero que buffer1 este listo (o sea, que DMA termine de traerme los datos solicitados)
4)empiezo a trabajar con buffer1. (buffer2 esta de camino mediante DMA, pero ni me preocupo)
5)cuando termino de trabajar con buffer1, programo la subida de resultados a RAM con otro DMA.
6)empiezo a bajarme otro trozo de trabajo mediante DMA en buffer3
7)espero que el DMA haya terminado con buffer2 y me pongo a trabajar con el.

y asi hasta el infinito, un buffer con resultados calculados, que se suben mediante DMA, otro buffer donde se trabaja, y otro buffer donde me estoy descargando datos para trabajar con ellos. los SPU tienen un controlador DMA que puede tener hasta 8 transferencias simultaneas por SPU (en subida o bajada).

arriba he puesto el ejemplo complicado, o sea, trabajando con triplebuffering, que es realmente como se le saca el jugo a los SPE, pero tambien se pueden programar en doble-bufering y single-buffer, incluso en modo 'metralleta' (con la enorme penalizacion que tiene, como te he dicho)
Era lo del hilo bloqueado en el PPU,gracias por la aclaracion,una cosa mas¿como harias para mejorar el rendimiento grafico si el hilo que conecta la SPU con el PPU se bloquea?aparte de eso cuando el SPU que calcula los graficos si tiene que tirar de memoria RAM para video¿que la coje de la RAM principal(que son 256 MB) o recurre a la swap que usa la RAM del RSX?
Un saludo.
Psmaniaco escribió:Era lo del hilo bloqueado en el PPU,gracias por la aclaracion,una cosa mas¿como harias para mejorar el rendimiento grafico si el hilo que conecta la SPU con el PPU se bloquea?aparte de eso cuando el SPU que calcula los graficos si tiene que tirar de memoria RAM para video¿que la coje de la RAM principal(que son 256 MB) o recurre a la swap que usa la RAM del RSX?
Un saludo.


si el hilo en la SPU se bloquea no hay nada que hacer, el programa esta muerto. por supuesto, podrias hacer algun tipo de watchdog, un timeout si el proceso de la SPU tarda mas tiempo del esperado y tal y tal, pero eso conlleva manejador de excepciones y comprobacion y recuperacion de errores, cosa que consume mucho tiempo al programador para un software que se supone va a ser ludico (o sea, en software critico y en tiempo real eso que comentas tendria sentido, pero en un software que te hace jugar al 3 en raya no tiene mucho sentido)

al ser renderizado 100% por software (GPU desabilitada en PS3-Linux), los SPU cogen de la RAM principal de 256MB. en general, todos los SPU trabajan sobre la RAM principal. se puede configurar el motor DMA para traerte datos de RAM de video, pero es aun mas lento que la RAM principal, de hecho, el SPU dedicado al hipervisor en ps3-linux tiene una rutina especifica que copia el contenido del framebuffer de RAM a la video-RAM para presentarlo en pantalla, asi que si ya tienes una SPU que hace ese trabajo, y ademas lo hace bien, dedicate a trabajar en la RAM principal.
Voy a tener que empaparme bien todo lo que dices,desde luego lo que decian de lo complejo que es programar este procesador no es moco de pavo,pero asi veremos lo que es capaz de hacer(ya que IBM presume tanto habra que comprobarlo jejeje XD ),en cuanto termine de preparar mis PS3 para esta tarea veremos a ver de lo que pueden hacer conectadas entre si(pero para eso todavia me falta tiempo para aprender a hacerlo).
Un saludo.
pregunta todo lo que quieras, pero yo personalmente, ahora que sony ha abandonado el linux en PS3 con la nueva PS3-slim, voy a abandonar el barco de desarrollo para CELL. solo si sony permite otheros en ps3-slim me pensare volver...
Yo mientras IBM y Toshiba sigan dando informacion y el SDK para el Cell seguire adelante,desde luego Sony la esta cagando a base bien con este tema,veremos si esta decision no les pasa factura tarde o temprano.
Un saludo.
Psmaniaco escribió:Yo mientras IBM y Toshiba sigan dando informacion y el SDK para el Cell seguire adelante,desde luego Sony la esta cagando a base bien con este tema,veremos si esta decision no les pasa factura tarde o temprano.
Un saludo.


ya les esta pasando factura, la gente esta migrando a Xbox360, donde ya se puede cargar linux y tienes 3 cores simetricos con instrucciones VMX128 (que son las que hacen los SPE del cell tan potente). por supuesto no tienes el brutal ancho de banda que tiene el CELL, pero menos da una piedra...
Me lo acabo de mirar.. y no entiendo naada, creo que me faltan cursoss de no se que para llegar a entender esto..xd [tadoramo] [tadoramo] [tadoramo] [tadoramo] [tadoramo] [tadoramo] [tadoramo] [tadoramo] [tadoramo] [tadoramo]
Sin duda un currada!!
Gracias me alegro de que te guste. ;)
Chapó acid, de los mejores tutoriales que he visto, y eso que no entiendo mucho de Scene en PS3 [+risas]
Gracias por las clases f5inet ;) ,ahora que se puede cargar ejecutables sin firmar en la Xbox 360 esperare a ver si adaptan el kernel del Linux al los 3 cores de la 360 y la GPU para usar el Linux en la 360,pero mientras tanto a centrarse en el Cell.
Un saludo.
Psmaniaco escribió:Gracias por las clases f5inet ;) ,ahora que se puede cargar ejecutables sin firmar en la Xbox 360 esperare a ver si adaptan el kernel del Linux al los 3 cores de la 360 y la GPU para usar el Linux en la 360,pero mientras tanto a centrarse en el Cell.
Un saludo.


ok. si no quieres perder yu tiempo, vete mirando los 'intrinsics' de los SPU. los intrinsics son librerias/funciones que te permiten usan el procesador vectorial VMX128 (el procesador vectorial VMX128 es el motor vectorial que esta en el interior de los SPE y el Xenon) sin necesidad de programarlo en ensamblador, sino con simple C. es algo que te valdra tanto para CELL como para XENON-360
Tendre que mirar a ver.
Un saludo.
Dioooos qué pr4 el tutorial. Muy bueno, pero tienes que meter cosas simplonas de C, hombre xD
Acid-burn escribió:todavía no esta terminado! tengo poco tiempo, lo terminaré en breve.


Juraría que estos apuntes son de la facultad de informática y llevan más de un año hechos.
Te ha faltado poner la fuente: Juan Peinador, muy buen profesor por cierto, me paso estos mismos apuntes hará poco menos de un año XD

edit: al parecer los has completado con otras fuentes. ¿Por cierto te conozco?
64 respuestas
1, 2