Processore CELL:
Memoria, IPC e
benchmarking
Vincenzo De Maio
matricola 0510200251
Nella precedente puntata:

Architettura del processore Cell

Introduzione al CellSDK 3.0

Sviluppo del programma “Hello, World!” sul
processore Cell
BONUS TRACK:

Come distruggere la playstation in maniera
efficiente
Sommario

Accesso alla memoria

Comunicazione inter-processore

Benchmarking
Interfacce di accesso alla
memoria

Channel Interface (SPU)
− Accesso LOCALE, a bassissima latenza (6 cicli di
clock in caso di accesso non bloccante)

MMIO (Memory Mapped I/O) Interface (PPE,
altre SPE)
− Accesso a tutte le MFC, mappando gli indirizzi
locali in indirizzi validi nell'intero spazio di sistema
Accesso alla memoria

Problema 1: Diversi domini di indirizzi
− Un indirizzo di memoria della PPE non ha senso
nelle SPE!

Problema 2: Limitazioni di accesso
− Le SPU possono accedere solo ai dati presenti nel
proprio Local Store
Un “Ripasso”: Il MFC
(Memory Flow Controller)

Lo “special guest” della giornata...

Rappresenta l'interfaccia che mette in
comunicazione la SPU con l'EIB (e di
conseguenza con la memoria centrale, gli altri
elementi del processore e i dispositivi di I/O)

Gestisce la comunicazione interprocessore
(mailbox, segnali, interrupt...) e ha al suo
interno un controller DMA
Domini di indirizzi

1 Dominio della memoria principale (Real
Address Space)

6 Domini di Local Store (1 per ogni SPE) (Local
Store Address Space)

Effective Address Space
Limitazioni di accesso

La PPE può accedere facilmente alle Local
Store, viceversa le SPE effettuano SOLO
accessi locali...

Come avviene l'invio di dati alla SPE?
Invio di dati alla SPE: l'idea

Tramite il DMA, posso tradurre l'indirizzo nel
contesto della PPE in un indirizzo “effettivo” ed
effettuare una copia di questi dati nella Local
Store
− NOTA:

La memoria della Local Store è limitata a 256 KB...
attenzione a non riempirla!

Una volta effettuate le operazioni necessarie, è
necessario effettuare una scrittura nel dominio della
memoria centrale
Comandi DMA
Interfaccia di accesso della PPE e della SPE
Comando                     FUNZIONE
PPE SPE
GET
GETF
GETB
PUT
PUTF
PUTB
spe_mfcio_get mfc_get
spe_mfcio_getf mfc_getf
spe_mfcio_getb mfc_getb
spe_mfcio_put mfc_put
spe_mfcio_putf mfc_putf
spe_mfcio_putb mfc_putb
Comandi DMA (SPE)

Parametri di un comando DMA
− void* lsa: indirizzo della local store (in cui
scrivere/leggere)
− uint64_t ea: l'indirizzo effettivo (in cui
scrivere/leggere)
− uint32_t size: il numero di byte da scrivere/leggere
− uint32_t tag: il tag di un gruppo di comandi DMA
− uint32_t tid, uint32_t rid: transfer class id,
replacement class id (solitamente settati a 0)
Altre funzioni necessarie
uint32_t mfc_tag_reserve(), per ottenere un tag
valido

uint32_t mfc_multi_tag_reserve(uint32_t n), per
ottenere una serie di n tag contigui (restituisce
il primo della serie)

mfc_write_tag_mask(uint32_t tag), indica il tag
da attendere

mfc_read_tag_status_*(), attende la
terminazione dei tag nella tag mask
Tutte queste funzioni, comprese mfc_get e
mfc_put sono nella libreria spu_mfcio.h
Un po' di astrazione...
#define wait_all_tag(tag) mfc_write_tag_mask(1<<tag);
mfc_read_tag_status_all()
#define wait_any_tag(tag) mfc_write_tag_mask(1<<tag);
mfc_read_tag_status_any();
mfc_write(volatile void* dest,unsigned int src, unsigned int tag, int size){
mfc_put(dest,src,size,tag,0,0);
wait_all_tag(tag);
}
mfc_read(volatile void* src,unsigned int dest,unsigned int tag,int size){
mfc_get(src,dest,size,tag,0,0);
wait_all_tag(tag);
}
Un po' di codice (PPE->SPE)
Dopo ben 12 slide al riguardo, ne sappiamo
abbastanza per implementare un semplice
programma che si occupi di inviare/ricevere dati
a/da una SPE...

La PPE invia un vettore e un valore numerico
alla SPE; la SPE aggiunge a ogni elemento del
vettore il valore

Shopping list:
− Invio di dati a una SPE (argp) 
− Utilizzo dei comandi DMA nel contesto SPE 
− BONUS: Aggiunta del valore in modalità SIMD 
Dati da inviare
#ifndef __control_block_h__
#define __control_block_h__
typedef struct _control_block {
unsigned int shift; //valore da aggiungere
unsigned int addr; //indirizzo
char pad[120]; //padding
} control_block;
#endif
Dichiarazione e invio del
vettore numerico (PPE)
#include <libspe2.h>
#include <malloc_align.h>
#define BUFF_SIZE 64
#define BUFF_DIM BUFF_SIZE * sizeof(int)
#define NUM_SPE 1
control_block cb __attribute__ ((aligned (128))); //struct da inviare
int *arr; //array da inizializzare
int main(){
int i;
arr = (int*)_malloc_align(BUFF_DIM,7);
cb.shift = 3;
cb.addr = (unsigned int) arr;
/* omesse dichiarazioni dei contesti, delle strutture necessarie ai
thread e del ciclo in cui i thread vengono avviati*/
arg[i].argp = &cb;
}
Ricezione del vettore (SPE)
volatile control_block cb __attribute__ ((aligned (128)));
int arr[BUFF_SIZE] __attribute__ ((aligned (128)));
int main(unsigned long long speid, unsigned long long argp, unsigned
long long envp){
int i;
unsigned int tag_id;
if((tag_id = mfc_tag_reserve())==MFC_TAG_INVALID){
printf("Impossibile riservare il tag!n");
return 1;
}
mfc_read(&cb,(unsigned int)argp,1,sizeof(cb));
mfc_read(arr,(unsigned int)cb.addr,tag_id,sizeof(arr));
vector_increment(arr,BUFF_SIZE,SIMD_MODE,cb.shift);
mfc_write(arr,(unsigned int*)cb.addr,tag_id,sizeof(arr));
return 0;
}
Di nuovo alla PPE...
//attendiamo la terminazione dei thread...
for(i=0;i<NUM_THREADS;i++){
pthread_join(thread[i],NULL);
destroy_spe(spe[i]);
}
//stampo i risultati
for(i=0;i<BUFF_SIZE;i++) printf("%d ",arr[i]);
Invio di dati, PPE<-SPE

Adesso vedremo come ricevere dati dalla SPE
nella PPE...

Un semplice programma che legge una stringa
salvata in una SPE

La SPE invia alla PPE un indirizzo da cui leggere

Shopping list:
− Ricezione di un indirizzo da una SPE 
− Scrittura di un dato leggendo dall'indirizzo
(spe_mfcio_*) 
Codice PPE
#include <libspe2.h>
uint32_t ls_offset; // spiazzamento dei dati nella Local Store
volatile char my_data[BUFF_SIZE] __attribute__ ((aligned(128))); //buffer dei
dati
int main(int argc, char *argv[]){
int ret;
uint32_t tag, status;
/* Omessa creazione dei thread e assegnamento del tag*/
do{
ret=spe_mfcio_put( spe_ctx, ls_offset, (void*)my_data, BUFF_SIZE,
tag, 0,0);
}
while( ret!=0);
ret = spe_mfcio_tag_status_read(spe_ctx,0,SPE_TAG_ALL, &status);
__lwsync();
}
Alcune considerazioni

Il modello di accesso alla memoria, pur
essendo a basso livello, è abbastanza “pulito”...

Molto performante, adatto al trattamento di
array, puntatori e grosse quantità di dati...

Ma se volessi inviare un semplice intero?
DMA vs IPC

In caso di invio di semplici interi a 32 bit, questo
approccio è perdente...

Inutile inviare 128 byte per riceverne 4... 

Meglio utilizzare qualcosa studiato
appositamente per questi casi 
Sommario

Accesso alla memoria

Comunicazione inter-processore
− Mailbox
− Segnali

Benchmarking
Mailbox

Un semplice meccanismo di comunicazione
interprocessore, studiato per l'invio di messaggi
a 32 bit

Altamente performante, specie per le SPE... ☺

Rischia però di sovraccaricare l'EIB, in caso di
polling... quindi attenzione!
Mailbox

Per SPE abbiamo:
− 2 outbound mailbox (per le interrupt e per la
comunicazione con la PPE e altre SPE) (1 entry)
− 1 inbound mailbox (per la ricezione di dati dalla
PPE o da altre SPE) (4 entries)

Per ogni mailbox abbiamo
− Counter: il numero di entries presenti
− Le mailbox sono implementate come code FIFO
Differenze tra le mailboxes
Comportamento
InboundOutbound
Counter
Decrementato  quando 
un  messaggio  viene 
letto,  incrementato 
quando  un  messaggio 
viene scritto
Incrementato quando la spu 
scrive  un  messaggio, 
decrementato  quando  un 
messaggio viene letto
Overrun
Quando la PPE prova a 
scrivere  e  la  fifo  è 
piena,  viene 
sovrascritta  l'ultima 
entry
Quando la SPU legge da un 
buffer  vuoto, resta bloccata 
in attesa di dati
La  SPU  si  blocca  nel 
caso  tenta  di  leggere 
un buffer vuoto, mentre 
la  PPE  non  si  blocca 
mai
Quando la SPU scrive in un 
buffer  pieno resta bloccata, 
invece la PPE si preoccupa 
solo  di  restituire  un  valore 
errato
API per l'utilizzo delle
mailboxes

PPE (MMIO Interface)
− spe_out_mbox_read(spe_context_ptr_t
spe,unsigned int *mbox_data,int count, unsigned int
behavior)
− spe_in_mbox_write(spe_context_ptr_t spe,
unsigned int *mbox,int count,unsigned int behavior)
− spe_in_mbox_status(spe_context_ptr_t spe)
− spe_out_mbox_status(spe_context_ptr_t spe)
API per l'utilizzo delle
mailboxes

SPE (Channel Interface)
− spu_write_out_mbox(uint32_t data)
− spu_read_in_mbox()
− spu_stat_in_mbox()
− spu_stat_out_mbox()
Echo, questo sconosciuto...
SPE
uint32_t data;
while(spu_stat_in_mbox()<entries){}
data = spu_in_mbox_read();
spu_write_out_mbox(data)
PPE
unsigned int data = 32,recvd;
while(spe_in_mbox_status(spe[0])<1){ //wait }
spe_write_in_mbox(spe[0
],data,1,SPE_MBOX_ALL_BLOCKING);
.........
spe_read_out_mbox(spe[0],&recvd,1);
??? manca qualcosa!!

Manca l'invio di messaggi diretto tra le SPE!

Con queste funzioni non possiamo inviare dati
senza passare per la PPE...

IDEA: conoscendo l'indirizzo di memoria della
mailbox, si potrebbero utilizzare le funzioni
MFC...
Implementazione della
comunicazione SPE-SPE con
le mailbox

Per ottenere l'accesso a determinate aree della
SPU, nella funzione libspe2.h abbiamo una
funzione apposita:
volatile spe_spu_control_area_t* ctl_area;
ctl_area = (spe_spu_control_area_t)* spe_ps_area_get(spe[i
],SPE_CONTROL_AREA);
uint64_t ctl_addr;
ctl_addr = (uint64_t)ctl_area;
while(spe_in_mbox_status(spe[i])<4){}
// invio dell'indirizzo alla SPE
spe_in_mbox_write(spe[i],(uint32_t*)&ctl_addr,2,SPE_MBOX_ALL_BLOCKING);
Ricezione dell'indirizzo
uint32_t ea_h,ea_l;
uint64_t eff_addr;
while(spu_stat_in_mbox()<2){}
ea_h=spu_read_in_mbox(); //32 bit più
significativi
ea_l=spu_read_in_mbox(); //32 bit meno
significativi
eff_addr =mfc_hl2ea(ea_h,ea_l);
//concatenazione
Scrittura in un'altra SPE (1)
#define SPU_IN_MBOX_OFFSET 0x0C
#define SPU_IN_MBOX_OFFSET_SLOT 0x3
#define SPU_MBOX_STAT_OFFSET 0x14
#define SPU_MBOX_STAT_OFFSET_SLOT 0x1 //alcuni dati di utilità
inline int status_at_mbox(uint64_t address,uint32_t tag){
uint32_t status[4],id;
uint64_t ea_stat_mbox = address + SPU_MBOX_STAT_OFFSET;
id = SPU_MBOX_STAT_OFFSET_SLOT;
mfc_get((void*)&status[id],ea_stat_mbox,sizeof(uint32_t),tag,0,0);
mfc_write_tag_mask(1<<tag);
mfc_read_tag_status_any();
return status[id];
}
inline int status_at_in_mbox(uint64_t address,uint32_t tag){
int status;
status = status_at_mbox(address,tag);
status = (status&0x0000ff00)>>8;
return status;
}
Scrittura in un'altra SPE (2)
inline int write_in_mbox(uint32_t data,uint64_t ea,uint32_t tag){
uint64_t ea_mailbox = ea + SPU_IN_MBOX_OFFSET;
uint32_t mbox[4],id;
int status;
while((status=status_at_in_mbox(ea,tag))<1);
id = SPU_IN_MBOX_OFFSET_SLOT;
mbox[id] = data;
mfc_put((void*)&mbox[id],ea_mailbox,sizeof(uint32_t),tag,0,0
);
mfc_write_tag_mask(1<<tag);
mfc_read_tag_status_any();
return 1;
}
Sommario

Accesso alla memoria

Comunicazione inter-processore
− Mailbox
− Segnali

Benchmarking
Segnali

A differenza dei segnali UNIX, nella CBEA i
segnali implementano un meccanismo molto
simile alle mailbox...

Per la gestione asincrona di eventi esiste una
gestione simile a UNIX...

Ma non la vedremo in questo seminario!
Segnali

Ogni SPE ha 2 registri a 32 bit per la
segnalazione, assolutamente identici

Consente l'invio di interi a 32 bit

La PPU effettua/riceve segnalazioni tramite la
MMIO interface, mentre la SPU utilizza la
Channel Interface per leggere i suoi registri
Segnali vs Mailbox

I segnali, a differenza delle mailbox, non hanno
“entries”...

Sono UNIDIREZIONALI

Consentono due diverse modalità di scrittura
− OR mode: le write vengono combinate attraverso
un'operazione di or bit a bit
− Overwrite mode: successive write sovrascrivono il
valore presente nel registro

Una lettura del counter restituisce solo 0, se non
ci sono segnali pendenti, o 1 se ce n'è almeno
uno.
API per l'utilizzo dei segnali

PPE
− spe_signal_write(spe_context_ptr_t spe,unsigned
int notification_registry,unsigned int data)
− spe_context_ptr_t spe_context_create(unsigned int
flags, spe_gang_context_ptr_t gang) (per utilizzare
la modalità OR, bisogna passare come flag
SPE_CFG_SIGNOTIFY_OR*

SPE
− uint32_t spu_read_signal*()
Segnali SPE<->SPE

È possibile sfruttare lo stesso principio pensato
per le mailbox per implementare la
segnalazione tra 2 SPE...

Tuttavia per brevità non la vedremo in questo
seminario!
Intervallo: I consigli della
nonna
Ascoltatemi, io
c' ho esperienza
Il soggetto della foto è maggiorenne e consenziente al trattamento dei dati personali ai
sensi della legge.
I consigli della nonna

Delegare quanto più possibile il lavoro alle SPE

Sfruttare il parallelismo
− A task separati corrispondono SPE separate
− Il numero dei thread non deve MAI superare il numero delle
SPE
− Non abusare dei threads, in quanto la loro creazione
sovraccarica il sistema

Utilizzare la precisione doppia SOLO se necessario

Cercare di ricorrere alle istruzioni di sincronizzazione il meno
possibile

Utilizzare la keyword volatile, al fine di indicare al compilatore di
non riordinare gli accessi di memoria ai buffer dichiarati in
questo modo
Sommario

Accesso alla memoria

Comunicazione inter-processore
− Mailbox
− Segnali

Benchmarking
Tutto questo a che pro?

Lo scopo ultimo del mio lavoro è il porting su
CELL di un programma di dinamica molecolare

Una semplice applicazione parallela, secondo il
paradigma SCATTER-PROCESS-GATHER
The making of...

Il programma originario è scritto in FORTRAN77
PRIMO PROBLEMA: Riutilizzare le funzioni
FORTRAN in un programma scritto in C
Riutilizzo delle subroutines
FORTRAN

Possibile?
− Si, da qualche parte tutto diventa assembly :)
− Basta compilare separatamente il file oggetto
(maggiori dettagli in seguito)

C'è solo bisogno di sapere alcune cose...
− Le funzioni C accettano il passaggio di parametri per
valore e per riferimento, mentre quelle fortran SOLO
per riferimento...
− In FORTRAN77 non esiste allocazione dinamica (ne'
tantomeno i puntatori!)
Riutilizzo delle subroutines
FORTRAN
FORTRAN77
a = 5
b = 3
subroutine add(a,b)
a+b
return
end
C che richiama FORTRAN77
int a=5,b=3;
add_(&a,&b);
Riutilizzo delle subroutines
FORTRAN
C
int a=5,b=3;
void add_(int *sum,int *a,int *b){
*sum = *a + *b;
}
FORTRAN77 che richiama C
call add(sum,a,b)
E le variabili?

Per comodità, nel programma FORTRAN77
tutte le variabili sono dichiarati all'interno di
common blocks (gli antenati delle struct...) in
un file .h

Equivalenti a una extern struct in C
Riutilizzo dei COMMON
BLOCKS
FORTRAN
real*4 alat,dt,dtforce,sigma,sigsq,cutsq1,cutsq2
common /blk01/
alat,dt,dtforce,sigma,sigsq,cutsq1,cutsq2
C che richiama FORTRAN
extern struct blk01_;
float alat = blk01_.alat;
Tutto finito?
Magari...

Nel FORTRAN77 non esiste allocazione
dinamica, quindi tutto viene preallocato
staticamente...

La SPE ha un limite di memoria di 256 KB, per
dati e istruzioni!

Secondo voi è sufficiente?
Dimensione dati

Un breve calcolo...
− 60 byte(per atomo) * 10000 (numero di atomi
preallocati) = 600000 byte = 585.9375KB
− 62 float = 62 * 4 byte = 248 byte
− Vari altri parametri utili al programma...
− Devo continuare?
Allocazione dinamica

In FORTRAN?
− Non esiste in FORTRAN77, ma è stata
implementata nelle versioni successive del
linguaggio e il compilatore spu-gfortran accetta
codice da FORTRAN77 a Fortran95
− Non può essere usata per i common blocks

Meglio in C...
− Elevata esperienza d'uso e facilità di gestione
Allocazione Dinamica

Soluzione prescelta:
− Variabili dichiarate in C, così come i puntatori da
allocare utilizzando la malloc...
− In seguito, effettuo il passaggio di array e variabili
alla funzione FORTRAN da utilizzare, dopo aver
modificato la stessa in modo che accetti tutti i valori
in input...
Come agire sul codice
FORTRAN INIZIALE
integer arr(30)
subroutine add_arr(a,b)
do i=1,30
arr(i) = a+b;
enddo
return
end
FORTRAN MODIFICATO
subroutine add(a,b,arr,dim_arr)
integer,intent(in)::dim_arr
integer,intent(inout),dimension(dim_arr)::arr
do i=1,30
arr(i) = a+b;
enddo
end
Tuttavia...

Nonostante questo, il programma era ancora
troppo grande per le SPE...

Elimino le funzioni di input/output mappando
questa fase sulla PPE

Ancora troppo grande!
Soluzione definitiva

Dopo averle pensate praticamente TUTTE,
compreso spu-strip e spu-readelf...

Commento una write() per effettuare un test e...
IL PROGRAMMA ENTRA NELLA SPE!
Mistero!

Il compilatore spu-gfortran, appena trovava
quella write, includeva staticamente l'intera
libreria di input/output di FORTRAN77...

Trasformando un programma di 160KB in un
programma di 660KB!

Morale della favola:
− Per le stampe a schermo necessarie alla gestione
degli errori, meglio richiamare una funzione scritta in
C dal programma FORTRAN77.
Il mio lavoro finora...

Suddivisione del lavoro in un'immagine SPE e
un'immagine PPE

Nell'immagine PPE effettuo l'input dei dati
necessari alla computazione, invio i dati alla
SPE e ne attendo la terminazione

Nell'immagine SPE inizializzo la porzione di
array assegnata ed effettuo il processing
basandomi SOLO sui dati locali
Cosa manca?

La comunicazione tra le varie SPE

Il gathering dei dati

Una migliore gestione della memoria
− Il programma riesce a gestire fino a poco più di 200
atomi per SPE... :'(
Risultati ottenuti finora

Abbiamo effettuato un test basandoci su questa
porzione di lavoro e confrontandola con il
programma originario “Modificato”

Non molto attendibile, visto che il maggiore
overhead è rappresentato proprio dalla
comunicazione e dal gathering...
Benchmarking
Conclusioni (per ora)

Non sono dati definitivi, ma considerando che
− Il programma CELL non ha ancora ottimizzazioni di
sorta
− Non utilizzo tecniche come il double buffering e le
estensioni SIMD, che velocizzano notevolmente il
trasferimento dei dati...

Penso che valga la pena approfondire questa
strada!
Riferimenti

CBEA Handbook

Programming the CBEA, Examples and Best
Practises
To be continued...
GRAZIE PER LA CORTESE ATTENZIONE

Cell Programming 2

  • 1.
    Processore CELL: Memoria, IPCe benchmarking Vincenzo De Maio matricola 0510200251
  • 2.
    Nella precedente puntata:  Architetturadel processore Cell  Introduzione al CellSDK 3.0  Sviluppo del programma “Hello, World!” sul processore Cell BONUS TRACK:  Come distruggere la playstation in maniera efficiente
  • 3.
    Sommario  Accesso alla memoria  Comunicazioneinter-processore  Benchmarking
  • 4.
    Interfacce di accessoalla memoria  Channel Interface (SPU) − Accesso LOCALE, a bassissima latenza (6 cicli di clock in caso di accesso non bloccante)  MMIO (Memory Mapped I/O) Interface (PPE, altre SPE) − Accesso a tutte le MFC, mappando gli indirizzi locali in indirizzi validi nell'intero spazio di sistema
  • 5.
    Accesso alla memoria  Problema1: Diversi domini di indirizzi − Un indirizzo di memoria della PPE non ha senso nelle SPE!  Problema 2: Limitazioni di accesso − Le SPU possono accedere solo ai dati presenti nel proprio Local Store
  • 6.
    Un “Ripasso”: IlMFC (Memory Flow Controller)  Lo “special guest” della giornata...  Rappresenta l'interfaccia che mette in comunicazione la SPU con l'EIB (e di conseguenza con la memoria centrale, gli altri elementi del processore e i dispositivi di I/O)  Gestisce la comunicazione interprocessore (mailbox, segnali, interrupt...) e ha al suo interno un controller DMA
  • 7.
    Domini di indirizzi  1Dominio della memoria principale (Real Address Space)  6 Domini di Local Store (1 per ogni SPE) (Local Store Address Space)  Effective Address Space
  • 8.
    Limitazioni di accesso  LaPPE può accedere facilmente alle Local Store, viceversa le SPE effettuano SOLO accessi locali...  Come avviene l'invio di dati alla SPE?
  • 9.
    Invio di datialla SPE: l'idea  Tramite il DMA, posso tradurre l'indirizzo nel contesto della PPE in un indirizzo “effettivo” ed effettuare una copia di questi dati nella Local Store − NOTA:  La memoria della Local Store è limitata a 256 KB... attenzione a non riempirla!  Una volta effettuate le operazioni necessarie, è necessario effettuare una scrittura nel dominio della memoria centrale
  • 10.
    Comandi DMA Interfaccia diaccesso della PPE e della SPE Comando                     FUNZIONE PPE SPE GET GETF GETB PUT PUTF PUTB spe_mfcio_get mfc_get spe_mfcio_getf mfc_getf spe_mfcio_getb mfc_getb spe_mfcio_put mfc_put spe_mfcio_putf mfc_putf spe_mfcio_putb mfc_putb
  • 11.
    Comandi DMA (SPE)  Parametridi un comando DMA − void* lsa: indirizzo della local store (in cui scrivere/leggere) − uint64_t ea: l'indirizzo effettivo (in cui scrivere/leggere) − uint32_t size: il numero di byte da scrivere/leggere − uint32_t tag: il tag di un gruppo di comandi DMA − uint32_t tid, uint32_t rid: transfer class id, replacement class id (solitamente settati a 0)
  • 12.
    Altre funzioni necessarie uint32_tmfc_tag_reserve(), per ottenere un tag valido  uint32_t mfc_multi_tag_reserve(uint32_t n), per ottenere una serie di n tag contigui (restituisce il primo della serie)  mfc_write_tag_mask(uint32_t tag), indica il tag da attendere  mfc_read_tag_status_*(), attende la terminazione dei tag nella tag mask Tutte queste funzioni, comprese mfc_get e mfc_put sono nella libreria spu_mfcio.h
  • 13.
    Un po' diastrazione... #define wait_all_tag(tag) mfc_write_tag_mask(1<<tag); mfc_read_tag_status_all() #define wait_any_tag(tag) mfc_write_tag_mask(1<<tag); mfc_read_tag_status_any(); mfc_write(volatile void* dest,unsigned int src, unsigned int tag, int size){ mfc_put(dest,src,size,tag,0,0); wait_all_tag(tag); } mfc_read(volatile void* src,unsigned int dest,unsigned int tag,int size){ mfc_get(src,dest,size,tag,0,0); wait_all_tag(tag); }
  • 14.
    Un po' dicodice (PPE->SPE) Dopo ben 12 slide al riguardo, ne sappiamo abbastanza per implementare un semplice programma che si occupi di inviare/ricevere dati a/da una SPE...  La PPE invia un vettore e un valore numerico alla SPE; la SPE aggiunge a ogni elemento del vettore il valore  Shopping list: − Invio di dati a una SPE (argp)  − Utilizzo dei comandi DMA nel contesto SPE  − BONUS: Aggiunta del valore in modalità SIMD 
  • 15.
    Dati da inviare #ifndef__control_block_h__ #define __control_block_h__ typedef struct _control_block { unsigned int shift; //valore da aggiungere unsigned int addr; //indirizzo char pad[120]; //padding } control_block; #endif
  • 16.
    Dichiarazione e inviodel vettore numerico (PPE) #include <libspe2.h> #include <malloc_align.h> #define BUFF_SIZE 64 #define BUFF_DIM BUFF_SIZE * sizeof(int) #define NUM_SPE 1 control_block cb __attribute__ ((aligned (128))); //struct da inviare int *arr; //array da inizializzare int main(){ int i; arr = (int*)_malloc_align(BUFF_DIM,7); cb.shift = 3; cb.addr = (unsigned int) arr; /* omesse dichiarazioni dei contesti, delle strutture necessarie ai thread e del ciclo in cui i thread vengono avviati*/ arg[i].argp = &cb; }
  • 17.
    Ricezione del vettore(SPE) volatile control_block cb __attribute__ ((aligned (128))); int arr[BUFF_SIZE] __attribute__ ((aligned (128))); int main(unsigned long long speid, unsigned long long argp, unsigned long long envp){ int i; unsigned int tag_id; if((tag_id = mfc_tag_reserve())==MFC_TAG_INVALID){ printf("Impossibile riservare il tag!n"); return 1; } mfc_read(&cb,(unsigned int)argp,1,sizeof(cb)); mfc_read(arr,(unsigned int)cb.addr,tag_id,sizeof(arr)); vector_increment(arr,BUFF_SIZE,SIMD_MODE,cb.shift); mfc_write(arr,(unsigned int*)cb.addr,tag_id,sizeof(arr)); return 0; }
  • 18.
    Di nuovo allaPPE... //attendiamo la terminazione dei thread... for(i=0;i<NUM_THREADS;i++){ pthread_join(thread[i],NULL); destroy_spe(spe[i]); } //stampo i risultati for(i=0;i<BUFF_SIZE;i++) printf("%d ",arr[i]);
  • 19.
    Invio di dati,PPE<-SPE  Adesso vedremo come ricevere dati dalla SPE nella PPE...  Un semplice programma che legge una stringa salvata in una SPE  La SPE invia alla PPE un indirizzo da cui leggere  Shopping list: − Ricezione di un indirizzo da una SPE  − Scrittura di un dato leggendo dall'indirizzo (spe_mfcio_*) 
  • 20.
    Codice PPE #include <libspe2.h> uint32_tls_offset; // spiazzamento dei dati nella Local Store volatile char my_data[BUFF_SIZE] __attribute__ ((aligned(128))); //buffer dei dati int main(int argc, char *argv[]){ int ret; uint32_t tag, status; /* Omessa creazione dei thread e assegnamento del tag*/ do{ ret=spe_mfcio_put( spe_ctx, ls_offset, (void*)my_data, BUFF_SIZE, tag, 0,0); } while( ret!=0); ret = spe_mfcio_tag_status_read(spe_ctx,0,SPE_TAG_ALL, &status); __lwsync(); }
  • 21.
    Alcune considerazioni  Il modellodi accesso alla memoria, pur essendo a basso livello, è abbastanza “pulito”...  Molto performante, adatto al trattamento di array, puntatori e grosse quantità di dati...  Ma se volessi inviare un semplice intero?
  • 22.
    DMA vs IPC  Incaso di invio di semplici interi a 32 bit, questo approccio è perdente...  Inutile inviare 128 byte per riceverne 4...   Meglio utilizzare qualcosa studiato appositamente per questi casi 
  • 23.
    Sommario  Accesso alla memoria  Comunicazioneinter-processore − Mailbox − Segnali  Benchmarking
  • 24.
    Mailbox  Un semplice meccanismodi comunicazione interprocessore, studiato per l'invio di messaggi a 32 bit  Altamente performante, specie per le SPE... ☺  Rischia però di sovraccaricare l'EIB, in caso di polling... quindi attenzione!
  • 25.
    Mailbox  Per SPE abbiamo: −2 outbound mailbox (per le interrupt e per la comunicazione con la PPE e altre SPE) (1 entry) − 1 inbound mailbox (per la ricezione di dati dalla PPE o da altre SPE) (4 entries)  Per ogni mailbox abbiamo − Counter: il numero di entries presenti − Le mailbox sono implementate come code FIFO
  • 26.
    Differenze tra lemailboxes Comportamento InboundOutbound Counter Decrementato  quando  un  messaggio  viene  letto,  incrementato  quando  un  messaggio  viene scritto Incrementato quando la spu  scrive  un  messaggio,  decrementato  quando  un  messaggio viene letto Overrun Quando la PPE prova a  scrivere  e  la  fifo  è  piena,  viene  sovrascritta  l'ultima  entry Quando la SPU legge da un  buffer  vuoto, resta bloccata  in attesa di dati La  SPU  si  blocca  nel  caso  tenta  di  leggere  un buffer vuoto, mentre  la  PPE  non  si  blocca  mai Quando la SPU scrive in un  buffer  pieno resta bloccata,  invece la PPE si preoccupa  solo  di  restituire  un  valore  errato
  • 27.
    API per l'utilizzodelle mailboxes  PPE (MMIO Interface) − spe_out_mbox_read(spe_context_ptr_t spe,unsigned int *mbox_data,int count, unsigned int behavior) − spe_in_mbox_write(spe_context_ptr_t spe, unsigned int *mbox,int count,unsigned int behavior) − spe_in_mbox_status(spe_context_ptr_t spe) − spe_out_mbox_status(spe_context_ptr_t spe)
  • 28.
    API per l'utilizzodelle mailboxes  SPE (Channel Interface) − spu_write_out_mbox(uint32_t data) − spu_read_in_mbox() − spu_stat_in_mbox() − spu_stat_out_mbox()
  • 29.
    Echo, questo sconosciuto... SPE uint32_tdata; while(spu_stat_in_mbox()<entries){} data = spu_in_mbox_read(); spu_write_out_mbox(data) PPE unsigned int data = 32,recvd; while(spe_in_mbox_status(spe[0])<1){ //wait } spe_write_in_mbox(spe[0 ],data,1,SPE_MBOX_ALL_BLOCKING); ......... spe_read_out_mbox(spe[0],&recvd,1);
  • 30.
    ??? manca qualcosa!!  Mancal'invio di messaggi diretto tra le SPE!  Con queste funzioni non possiamo inviare dati senza passare per la PPE...  IDEA: conoscendo l'indirizzo di memoria della mailbox, si potrebbero utilizzare le funzioni MFC...
  • 31.
    Implementazione della comunicazione SPE-SPEcon le mailbox  Per ottenere l'accesso a determinate aree della SPU, nella funzione libspe2.h abbiamo una funzione apposita: volatile spe_spu_control_area_t* ctl_area; ctl_area = (spe_spu_control_area_t)* spe_ps_area_get(spe[i ],SPE_CONTROL_AREA); uint64_t ctl_addr; ctl_addr = (uint64_t)ctl_area; while(spe_in_mbox_status(spe[i])<4){} // invio dell'indirizzo alla SPE spe_in_mbox_write(spe[i],(uint32_t*)&ctl_addr,2,SPE_MBOX_ALL_BLOCKING);
  • 32.
    Ricezione dell'indirizzo uint32_t ea_h,ea_l; uint64_teff_addr; while(spu_stat_in_mbox()<2){} ea_h=spu_read_in_mbox(); //32 bit più significativi ea_l=spu_read_in_mbox(); //32 bit meno significativi eff_addr =mfc_hl2ea(ea_h,ea_l); //concatenazione
  • 33.
    Scrittura in un'altraSPE (1) #define SPU_IN_MBOX_OFFSET 0x0C #define SPU_IN_MBOX_OFFSET_SLOT 0x3 #define SPU_MBOX_STAT_OFFSET 0x14 #define SPU_MBOX_STAT_OFFSET_SLOT 0x1 //alcuni dati di utilità inline int status_at_mbox(uint64_t address,uint32_t tag){ uint32_t status[4],id; uint64_t ea_stat_mbox = address + SPU_MBOX_STAT_OFFSET; id = SPU_MBOX_STAT_OFFSET_SLOT; mfc_get((void*)&status[id],ea_stat_mbox,sizeof(uint32_t),tag,0,0); mfc_write_tag_mask(1<<tag); mfc_read_tag_status_any(); return status[id]; } inline int status_at_in_mbox(uint64_t address,uint32_t tag){ int status; status = status_at_mbox(address,tag); status = (status&0x0000ff00)>>8; return status; }
  • 34.
    Scrittura in un'altraSPE (2) inline int write_in_mbox(uint32_t data,uint64_t ea,uint32_t tag){ uint64_t ea_mailbox = ea + SPU_IN_MBOX_OFFSET; uint32_t mbox[4],id; int status; while((status=status_at_in_mbox(ea,tag))<1); id = SPU_IN_MBOX_OFFSET_SLOT; mbox[id] = data; mfc_put((void*)&mbox[id],ea_mailbox,sizeof(uint32_t),tag,0,0 ); mfc_write_tag_mask(1<<tag); mfc_read_tag_status_any(); return 1; }
  • 35.
    Sommario  Accesso alla memoria  Comunicazioneinter-processore − Mailbox − Segnali  Benchmarking
  • 36.
    Segnali  A differenza deisegnali UNIX, nella CBEA i segnali implementano un meccanismo molto simile alle mailbox...  Per la gestione asincrona di eventi esiste una gestione simile a UNIX...  Ma non la vedremo in questo seminario!
  • 37.
    Segnali  Ogni SPE ha2 registri a 32 bit per la segnalazione, assolutamente identici  Consente l'invio di interi a 32 bit  La PPU effettua/riceve segnalazioni tramite la MMIO interface, mentre la SPU utilizza la Channel Interface per leggere i suoi registri
  • 38.
    Segnali vs Mailbox  Isegnali, a differenza delle mailbox, non hanno “entries”...  Sono UNIDIREZIONALI  Consentono due diverse modalità di scrittura − OR mode: le write vengono combinate attraverso un'operazione di or bit a bit − Overwrite mode: successive write sovrascrivono il valore presente nel registro  Una lettura del counter restituisce solo 0, se non ci sono segnali pendenti, o 1 se ce n'è almeno uno.
  • 39.
    API per l'utilizzodei segnali  PPE − spe_signal_write(spe_context_ptr_t spe,unsigned int notification_registry,unsigned int data) − spe_context_ptr_t spe_context_create(unsigned int flags, spe_gang_context_ptr_t gang) (per utilizzare la modalità OR, bisogna passare come flag SPE_CFG_SIGNOTIFY_OR*  SPE − uint32_t spu_read_signal*()
  • 40.
    Segnali SPE<->SPE  È possibilesfruttare lo stesso principio pensato per le mailbox per implementare la segnalazione tra 2 SPE...  Tuttavia per brevità non la vedremo in questo seminario!
  • 41.
    Intervallo: I consiglidella nonna Ascoltatemi, io c' ho esperienza Il soggetto della foto è maggiorenne e consenziente al trattamento dei dati personali ai sensi della legge.
  • 42.
    I consigli dellanonna  Delegare quanto più possibile il lavoro alle SPE  Sfruttare il parallelismo − A task separati corrispondono SPE separate − Il numero dei thread non deve MAI superare il numero delle SPE − Non abusare dei threads, in quanto la loro creazione sovraccarica il sistema  Utilizzare la precisione doppia SOLO se necessario  Cercare di ricorrere alle istruzioni di sincronizzazione il meno possibile  Utilizzare la keyword volatile, al fine di indicare al compilatore di non riordinare gli accessi di memoria ai buffer dichiarati in questo modo
  • 43.
    Sommario  Accesso alla memoria  Comunicazioneinter-processore − Mailbox − Segnali  Benchmarking
  • 44.
    Tutto questo ache pro?  Lo scopo ultimo del mio lavoro è il porting su CELL di un programma di dinamica molecolare  Una semplice applicazione parallela, secondo il paradigma SCATTER-PROCESS-GATHER
  • 45.
    The making of...  Ilprogramma originario è scritto in FORTRAN77 PRIMO PROBLEMA: Riutilizzare le funzioni FORTRAN in un programma scritto in C
  • 46.
    Riutilizzo delle subroutines FORTRAN  Possibile? −Si, da qualche parte tutto diventa assembly :) − Basta compilare separatamente il file oggetto (maggiori dettagli in seguito)  C'è solo bisogno di sapere alcune cose... − Le funzioni C accettano il passaggio di parametri per valore e per riferimento, mentre quelle fortran SOLO per riferimento... − In FORTRAN77 non esiste allocazione dinamica (ne' tantomeno i puntatori!)
  • 47.
    Riutilizzo delle subroutines FORTRAN FORTRAN77 a= 5 b = 3 subroutine add(a,b) a+b return end C che richiama FORTRAN77 int a=5,b=3; add_(&a,&b);
  • 48.
    Riutilizzo delle subroutines FORTRAN C inta=5,b=3; void add_(int *sum,int *a,int *b){ *sum = *a + *b; } FORTRAN77 che richiama C call add(sum,a,b)
  • 49.
    E le variabili?  Percomodità, nel programma FORTRAN77 tutte le variabili sono dichiarati all'interno di common blocks (gli antenati delle struct...) in un file .h  Equivalenti a una extern struct in C
  • 50.
    Riutilizzo dei COMMON BLOCKS FORTRAN real*4alat,dt,dtforce,sigma,sigsq,cutsq1,cutsq2 common /blk01/ alat,dt,dtforce,sigma,sigsq,cutsq1,cutsq2 C che richiama FORTRAN extern struct blk01_; float alat = blk01_.alat; Tutto finito?
  • 51.
    Magari...  Nel FORTRAN77 nonesiste allocazione dinamica, quindi tutto viene preallocato staticamente...  La SPE ha un limite di memoria di 256 KB, per dati e istruzioni!  Secondo voi è sufficiente?
  • 52.
    Dimensione dati  Un brevecalcolo... − 60 byte(per atomo) * 10000 (numero di atomi preallocati) = 600000 byte = 585.9375KB − 62 float = 62 * 4 byte = 248 byte − Vari altri parametri utili al programma... − Devo continuare?
  • 53.
    Allocazione dinamica  In FORTRAN? −Non esiste in FORTRAN77, ma è stata implementata nelle versioni successive del linguaggio e il compilatore spu-gfortran accetta codice da FORTRAN77 a Fortran95 − Non può essere usata per i common blocks  Meglio in C... − Elevata esperienza d'uso e facilità di gestione
  • 54.
    Allocazione Dinamica  Soluzione prescelta: −Variabili dichiarate in C, così come i puntatori da allocare utilizzando la malloc... − In seguito, effettuo il passaggio di array e variabili alla funzione FORTRAN da utilizzare, dopo aver modificato la stessa in modo che accetti tutti i valori in input...
  • 55.
    Come agire sulcodice FORTRAN INIZIALE integer arr(30) subroutine add_arr(a,b) do i=1,30 arr(i) = a+b; enddo return end FORTRAN MODIFICATO subroutine add(a,b,arr,dim_arr) integer,intent(in)::dim_arr integer,intent(inout),dimension(dim_arr)::arr do i=1,30 arr(i) = a+b; enddo end
  • 56.
    Tuttavia...  Nonostante questo, ilprogramma era ancora troppo grande per le SPE...  Elimino le funzioni di input/output mappando questa fase sulla PPE  Ancora troppo grande!
  • 57.
    Soluzione definitiva  Dopo averlepensate praticamente TUTTE, compreso spu-strip e spu-readelf...  Commento una write() per effettuare un test e... IL PROGRAMMA ENTRA NELLA SPE!
  • 58.
    Mistero!  Il compilatore spu-gfortran,appena trovava quella write, includeva staticamente l'intera libreria di input/output di FORTRAN77...  Trasformando un programma di 160KB in un programma di 660KB!  Morale della favola: − Per le stampe a schermo necessarie alla gestione degli errori, meglio richiamare una funzione scritta in C dal programma FORTRAN77.
  • 59.
    Il mio lavorofinora...  Suddivisione del lavoro in un'immagine SPE e un'immagine PPE  Nell'immagine PPE effettuo l'input dei dati necessari alla computazione, invio i dati alla SPE e ne attendo la terminazione  Nell'immagine SPE inizializzo la porzione di array assegnata ed effettuo il processing basandomi SOLO sui dati locali
  • 60.
    Cosa manca?  La comunicazionetra le varie SPE  Il gathering dei dati  Una migliore gestione della memoria − Il programma riesce a gestire fino a poco più di 200 atomi per SPE... :'(
  • 61.
    Risultati ottenuti finora  Abbiamoeffettuato un test basandoci su questa porzione di lavoro e confrontandola con il programma originario “Modificato”  Non molto attendibile, visto che il maggiore overhead è rappresentato proprio dalla comunicazione e dal gathering...
  • 62.
  • 63.
    Conclusioni (per ora)  Nonsono dati definitivi, ma considerando che − Il programma CELL non ha ancora ottimizzazioni di sorta − Non utilizzo tecniche come il double buffering e le estensioni SIMD, che velocizzano notevolmente il trasferimento dei dati...  Penso che valga la pena approfondire questa strada!
  • 64.
    Riferimenti  CBEA Handbook  Programming theCBEA, Examples and Best Practises
  • 65.
    To be continued... GRAZIEPER LA CORTESE ATTENZIONE