cell programming 1
DESCRIPTION
Il mio primo seminario in isislabTRANSCRIPT
Introduzione alla playstation 3
Vincenzo De Maiomatricola 0510200251
Sommario
● PS3: Cluster o console?● Architettura CELL● Programmazione usando il CELL
PS3: Cluster o console?
● Specifiche:– CPU: Processore CELL– GPU: RSX– Memoria: 256Mb XDR Ram principale, 256Mb
GDDR3 VRAM– Hard disk: SATA 2.5'' (dimensioni variabili a
seconda del modello) – Svariate altre periferiche, a seconda del modello
PS3: Cluster o console?
● Dal sito della IBM: ”Cell combines a general-purpose Power Architecture core of modest performance with streamlined coprocessing elements which greatly accelerate multimedia and vector processing applications, as well as many other forms of dedicated computation.”
● “ Though sold as a game console, what will in fact enter the home is a Cell-based computer. ” - Ken Kutaragi, padre della console playstation
PS3: Cluster o console?
RISPOSTA: CLUSTER E CONSOLE!
● Accontenta i videogiocatori, fornendo loro una notevole esperienza videoludica...
● Fornisce un cluster di tutto rispetto per un prezzo relativamente economico
Alcune informazioni
● Cell: abbreviazione di Cell Broadband Engine Architecture (talvolta si trova anche la sigla CBEA)
● Progettato in cooperazione da IBM, Sony e Toshiba espressamente per la PS3
Sommario
● PS3: Cluster o console?● Architettura CELL● Programmazione usando il CELL
Architettura CELL
Architettura CELL
● 1 Power Processor Element (PPE)● 8 Synergistic Processor Element (SPE)● Accesso alla memoria
PPE: Power Processor Element
● ”A core to rule them all...”● Architettura PowerPC a 64 bit 4GHZ (3.2 per la PS3)● Cache L1: 32KB per le istruzioni, 32KB per i dati● Cache L2: 512 KB● Si occupa di mappare i tasks alle SPE● Su di essa viene eseguito il sistema operativo...● ... il maggior carico computazionale viene sopportato
dalle SPE● Dual-threaded● in-order
PPE: Power Processor Element
● Perchè in-order?– Per poter fornire le capacità di esecuzione out-
of-order, c'è bisogno ovviamente di una maggiore complessità architetturale
– Lo scheduling delle istruzioni è totale responsabilità del compilatore...
Architettura CELL
● 1 Power Processor Element (PPE)● 8 Synergistic Processor Element (SPE)● Accesso alla memoria
SPE: Synergistic Processor Element
SPE:Synergistic Processor Element
● Le SPE rappresentano il ”braccio armato” della PPE...
● Architettura SIMD● Si prendono sulle loro spalle il maggior
carico computazionale● 128 registri a 128 bit● 4 floating point unit capaci di 32GFLOPS e 4
integer unit capaci di 32 GOPS● Local store di 256KB... Manca qualcosa?
SPE:Synergistic Processor Element
● Eh? manca la cache!● Motivazioni di questa scelta:
– Aggiunge complicazioni, dovute alla natura stessa della cache
– Possiamo usare il Local Store...
SPE:Synergistic Processor Element
● LOCAL STORE– Tramite questo la SPE accede alla memoria
(non può farlo direttamente)– Possiamo spostare 16byte(128 bit) per ciclo da e
verso la local store... esattamente il carico di ogni registro!
– A differenza della cache, la local store può sostenere questo carico per oltre 10000 cicli
SPE:Synergistic Processor Element
● Un possibile problema: contesa– Dobbiamo contemporaneamente caricare dati
dalla memoria, scrivere dati in memoria e scrivere/caricare dati nei/dai registri...
● Soluzione:– La local store accede alla memoria in blocchi di
almeno 1024bit per ciclo (circa 0.5 tera al secondo!)
SPE:Synergistic Processor Element
● Local Store vs CACHE– Un tipico esempio: audio processing
● Se riesco a caricare all'interno del Local Store l'intero blocco di audio su cui devo lavorare, annullo gli accessi alla memoria durante l'esecuzione del programma...
● Impossibile (o comunque estremamente difficile) farlo con la cache
SPE: Synergistic Processor Element
● Ulteriori vantaggi delle SPE– Possono essere usate efficacemente per stream
programming... (GPU)
Pensandoci su...
● Perchè tutto questo funzioni, c'è bisogno che le SPU siano sempre riempite di dati...
● Necessità di un sistema di accesso alla memoria e alle periferiche di I/O altamente performante...
Architettura CELL
● 1 Power Processor Element (PPE)● 8 Synergistic Processor Element (SPE)● Accesso alla memoria
– EIB e MMU– Memorie e dispositivi di I/O
Element Interconnect Bus
● Unisce tutte le componenti sul chip● Composto di 4 anelli da 16byte ciascuno e
permettono di effettuare fino a 3 trasferimenti simultanei.
● Cosa fa?– Si occupa di spostare dati dalla memoria
principale alle SPE e viceversa– Smista le istruzioni della PPE alle varie SPE
Element Interconnect Bus
● Prestazioni:– 96 bytes per ciclo di clock, anche se secondo
l'IBM ne sarebbero disponibili solo 64 nella pratica
● E la protezione della memoria?– Gestita dalle MMU contenute nelle SPE
Memory Management Unit
● Utilizzate dalle SPE per l'accesso alla memoria o per accedere agli altri Local Store
● Forniscono i meccanismi di coerenza e protezione della memoria
Importanza di EIB e MMU
● Entrambi devono essere performanti...● Ma sufficientemente complessi da riuscire a
gestire un enorme flusso di dati da distribuire in diverse destinazioni e una coda di almeno 128 richieste di memoria
Architettura CELL
● 1 Power Processor Element (PPE)● 8 Synergistic Processor Element (SPE)● Accesso alla memoria
– EIB e MMU– Memorie e dispositivi di I/O
Memorie e dispositivi di I/O
● Dati tecnici:– XDR Rambus 256MB– Controller FlexIO– Ampiezza di banda della memoria: 25.6 GB/sec– Ampiezza di banda dei dispositivi di I/O: 76.8
GB/sec
Intervallo
● Dolorosi incidenti di percorso... (vedi anche: ”la zappa sui piedi”)– come ho distrutto la playstation (1)– come ho distrutto la playstation (2)
Come ho distrutto la playstation (1)
● Configurazione del sistema: Yellow Dog Linux 5.0● Necessario installare il cellsdk, ma a ogni tentativo
ricevo un errore ”can't update glibc because of unresolved dependencies”
● IDEA: rimuovo le dipendenze, tanto ”se tolgo qualcosa di importante il sistema mi avviserà...”
DON'T TRY THIS AT HOME!!!
Come ho distrutto la playstation (1)
● A fianco vedete il risultato...
● Soluzione: formattazione e installazione di Fedora 5
Come ho distrutto la playstation (2)
● Installata fedora 5, ma il cellsdk3.0 richiede fedora 7!
● Piano piano, aggiorno glibc dalla versione della 5 alla versione della 7...
● installo il cellsdk...● applico varie patch al kernel...● compilo ed eseguo con successo vari programmi...● il tutto senza MAI riavviare la playstation 3!
DON'T TRY THIS AT HOME!!
Come ho distrutto la playstation (2)
● La mia espressione al successivo riavvio della playstation
● Soluzione: installata fedora 7 e il cellsdk seguendo le ”procedure standard”
Sommario
● PS3: Cluster o console?● Architettura CELL● Programmazione usando il CELL
Una citazione doverosa...
“To put it quite bluntly: as long as there were nomachines, programming was no problem at all;
when we had a few weak computers,programming became a mild problem, and nowwe have gigantic computers, programming has
become an equally gigantic problem." -- E. Dijkstra, 1972 Turing Award Lecture
Sarà vero??
Programmare con il Cell
● Ingredienti:– Conoscerne l'architettura– Tanta pazienza– Linux (Fedora 7.0 o Red Hat Enterprise 5.0)– IBM CellSDK 3.0
CellSDK
● Scaricabile dal sito della IBM (www.ibm.com)● Fornisce un ambiente di sviluppo per la
programmazione con il Cell, compatibile con qualsiasi architettura (purchè abbia FC 7 o RHE 5 installato)
CellSDK
● STRUMENTI DI SVILUPPO– libspe.h, libspe2.h– ppu-gcc– spu-gcc– ppu-embedspu
● STRUMENTI DI DEBUGGING– ppu-gdb– spu-gdb
● ALTRO– systemsim-cell
Altri tools utili non inclusi nell'sdk
● Eclipse + plugin per il cellsdk (richiede la JVM della IBM)
● Anjuta o Kdevelop
Hello, Cell!
● Obiettivo: scrivere il classico ”Hello, world” utilizzando tutti le SPE del cell.1) Isolare il task che ogni SPE dovrà eseguire (in
questo caso stampare la stringa ”Hello, world da spe_id” (dove spe_id è l'id della SPE in esadecimale)
2) Fare in modo che il PPE mappi il task giusto ad ogni SPE
Hello, Cell!
● Il primo passo è abbastanza banale...
#include <stdio.h>
int main(unsigned long long spe_id, unsigned long long argp, unsigned long long envp){
printf(”Hello, world! da 0x%x\n”,(unsigned int) spe_id);return 0;
}
#include <stdio.h>
int main(unsigned long long spe_id, unsigned long long argp, unsigned long long envp){
printf(”Hello, world! da 0x%x\n”,(unsigned int) spe_id);return 0;
}
Hello, Cell!
● Passo 2: iniziano i guai...– come indichiamo alla PPE il programma da
includere?– come effettuiamo il mapping ai vari SPE?
Hello, Cell!
● I programmi che vanno eseguiti dalle SPE sono scritti per lavorare insieme alla PPE, ma hanno vengono compilati indipendentemente
● 1 compilatore per i programmi della PPE e 1 per i programmi della SPE, rispettivamente ppu-gcc e spu-gcc
Hello, Cell!
● spu-gcc produce delle immagini ELF (Extensible Linking Format) che possono essere incluse nei programmi della PPE
● Ogni immagine ha la propria sezione di dati e di codice
● Le funzioni di Input/Output vengono eseguite richiedendo il servizio alla PPE
Hello, Cell!
extern spe_program_handle spe_program;extern spe_program_handle spe_program;
Ecco come dichiareremo il programma ”esterno” da inserire nelle SPE all'interno della PPE
Ora... bisogna dire alle SPE di caricare questo programma!
Hello, Cell!
● Soluzione: potremmo dire a ogni PPE di eseguire un thread corrispondente al programma sopra descritto
● In libspe.h, esistono funzioni apposite...– spe_create_thread() - crea un thread che
esegue al suo interno il programma– spe_wait() - aspetta la terminazione di un thread
Hello, Cell!
#include <stdio.h>#include <libspe2.h>#define NUM_THREAD 6extern spe_program_handle spe_program;int main(void){
spe_id_t[NUM_THREAD] spe_id;int i;//creiamo i thread...for(i=0;i<NUM_THREAD;i++){
id[i] = spe_create_thread(0,&spe_program,NULL,NULL,-1,0);}//attendiamo la loro terminazionefor(i=0;i<NUM_THREAD;i++){
spe_wait(id[i],NULL,0);}
}
#include <stdio.h>#include <libspe2.h>#define NUM_THREAD 6extern spe_program_handle spe_program;int main(void){
spe_id_t[NUM_THREAD] spe_id;int i;//creiamo i thread...for(i=0;i<NUM_THREAD;i++){
id[i] = spe_create_thread(0,&spe_program,NULL,NULL,-1,0);}//attendiamo la loro terminazionefor(i=0;i<NUM_THREAD;i++){
spe_wait(id[i],NULL,0);}
}
Hello, Cell!
● spe_create_thread(thread_group, program_handle,argp,envp)
● spe_wait()● TUTTO QUI?
Hello, Cell!
NO!LIBSPE DEPRECATA!
Bisogna usare libspe2 in combinazione con la ben nota pthread.h
Hello, Cell!
● Riscriviamo il programma usando la libspe2...
● A livello concettuale, vale tutto quello che abbiamo detto finora...
● semplicemente, siamo costretti a usare la pthread per la gestione dei threads
Hello, Cell!
● Creazione/distruzione dei thread– pthread_create(id,thread_attr,thread_func,func_arg);– pthread_join(id,value);
● Più alcuni amici da libspe2...– spe_context_create(flags,gang_context)– spe_program_load(spe_context_ptr,program_handle
)– spe_context_run(spe_context_ptr,entry_point,runflag
s,argp,envp,stopinfo)– spe_context_destroy(spe_context_ptr)
Amici da libspe2
● spe_context_create(flags,gang_context)– flags:
● SPE_EVENTS_ENABLE: abilita la gestione degli eventi
●
– gang_context:● equivalente al thread group, viene creato usando
spe_gang_context_create
Crea il context in cui viene eseguito il programma esterno
Amici da libspe2
● spe_program_load(spe_context_ptr,program_handle)– spe_context_ptr: puntatore al context spe– program_handle: puntatore al programma da
eseguire all'interno del context specificato
Carica il programma all'interno del context specificato
Amici da libspe2
● spe_context_run(spe_context_ptr,entry_point,runflags,argp,envp,stopinfo)– spe_context_ptr: puntatore allo spe_context da
eseguire– entry_point: parametro inout
● in: il valore dell'instruction pointer da cui il programma inizierà la sua esecuzione
● out: il valore dell'instruction pointer al momento della terminazione del programma
Amici da libspe2
● spe_context_run(spe_context_ptr,entry_point,runflags,argp,envp,stopinfo)– runflags: or bitwise di più costanti
● 0: comportamento di default● SPE_NO_CALLBACKS: vieta l'esecuzione
automatica di callbacks● SPE_RUN_USER_REGS: inizializza i 3 registri della
SPU con i 48bytes puntati da argp– argp: (OPZIONALE) puntatore a dati del
programma passati come secondo argomento
Amici da libspe2
● spe_context_run(spe_context_ptr,entry_point,runflags,argp,envp,stopinfo)– envp: (OPZIONALE) puntatore a dati specifici
dell'ambiente di esecuzione, passati come terzo parametro al programma
– stopinfo: (OPZIONALE) puntatore a una struttura spe_stop_info_t in cui scriveremo i dati relativi alla terminazione del programma
Amici da libspe2
● spe_context_destroy(spe_context_ptr)– libera le risorse associate a uno spe_context
Allora, programmiamo?
typedef struct{spe_context_ptr_t spe_id;unsigned long *args;
} thread_arg_t; //wrapping degli argomenti della funzionevoid *run_program(void* thread_arg){
unsigned int entry;entry = SPE_DEFAULT_ENTRY;spe_stop_info_t stop_info;ret = spe_context_run(arg->spe,&entry,0,NULL,NULL,&stop_info);if(ret<0){
perror("Errore di context_run");return NULL;
}printf("status %d\n",spe_stop_info_read(arg->spe,&stop_info));return NULL;
}
typedef struct{spe_context_ptr_t spe_id;unsigned long *args;
} thread_arg_t; //wrapping degli argomenti della funzionevoid *run_program(void* thread_arg){
unsigned int entry;entry = SPE_DEFAULT_ENTRY;spe_stop_info_t stop_info;ret = spe_context_run(arg->spe,&entry,0,NULL,NULL,&stop_info);if(ret<0){
perror("Errore di context_run");return NULL;
}printf("status %d\n",spe_stop_info_read(arg->spe,&stop_info));return NULL;
}
Allora, programmiamo?
int main(int argc,char **argv){int i,ret_value;spe_context_ptr_t spe[NUM_THREADS];pthread_t thread[NUM_THREADS];thread_arg_t arg[NUM_THREADS];for(i=0;i<NUM_THREADS;i++){
spe[i] = spe_context_create(SPE_EVENTS_ENABLE,NULL);if(!spe[i]){perror("spe_context_create error \n");exit(1);}ret = spe_program_load(spe[i],&hello_spu);if(ret){perror("errore di spe_program_load\n");exit(1);}//... CONTINUA
int main(int argc,char **argv){int i,ret_value;spe_context_ptr_t spe[NUM_THREADS];pthread_t thread[NUM_THREADS];thread_arg_t arg[NUM_THREADS];for(i=0;i<NUM_THREADS;i++){
spe[i] = spe_context_create(SPE_EVENTS_ENABLE,NULL);if(!spe[i]){perror("spe_context_create error \n");exit(1);}ret = spe_program_load(spe[i],&hello_spu);if(ret){perror("errore di spe_program_load\n");exit(1);}//... CONTINUA
Allora, programmiamo?arg[i].spe = spe[i];arg[i].args = NULL;ret = pthread_create(&thread[i],NULL,run_spu_program,&arg[i]);if(ret){perror("errore di pthread_create\n");exit(1);}
}for(i=0;i<NUM_THREADS;i++){pthread_join(thread[i],NULL);ret = spe_context_destroy(spe[i]);if(ret){
perror("errore di spe_context_destroy!\n");exit(1);
}}return 0;
}
arg[i].spe = spe[i];arg[i].args = NULL;ret = pthread_create(&thread[i],NULL,run_spu_program,&arg[i]);if(ret){perror("errore di pthread_create\n");exit(1);}
}for(i=0;i<NUM_THREADS;i++){pthread_join(thread[i],NULL);ret = spe_context_destroy(spe[i]);if(ret){
perror("errore di spe_context_destroy!\n");exit(1);
}}return 0;
}
Compilazione
● The ”old way”– spu-gcc -o hello_spu hello_spu.c -g– ppu-embedspu hello_spu hello_spu hello_spu-
embed.o– ar –qcs hello_spu.a hello_spu-embed.o– ppu-gcc -o hello hello_ppu.c -lspe2 hello_spu.a– ./hello
● Makefile– compreso nel cellsdk
Funzioni di accesso alla memoria
● int spe_mfcio_put (spe_context_ptr_t spe, unsigned int lsa, void *ea, unsigned int size, unsigned int tag, unsigned int tid, unsigned int rid)
● int spe_mfcio_get (spe_context_ptr_t spe, unsigned int lsa, void *ea, unsigned int size, unsigned int tag, unsigned int tid, unsigned int rid)
Comunicazione tra 2 SPE
● int spe_out_mbox_read (spe_context_ptr_t spe, unsigned int *mbox_data, int count)
● int spe_in_mbox_write (spe_context_ptr_t spe, unsigned int *mbox_data, int count, unsigned int behavior)
To be continued...
GRAZIE PER LA CORTESE ATTENZIONE