Cell Programming 2

65
Processore CELL: Memoria, IPC e benchmarking Vincenzo De Maio matricola 0510200251

description

Secondo seminario in isislab

Transcript of Cell Programming 2

Page 1: Cell Programming 2

Processore CELL: Memoria, IPC e benchmarking

Vincenzo De Maiomatricola 0510200251

Page 2: Cell Programming 2

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

Page 3: Cell Programming 2

Sommario

Accesso alla memoria

Comunicazione inter-processore

Benchmarking

Page 4: Cell Programming 2

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

Page 5: Cell Programming 2

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

Page 6: Cell Programming 2

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

Page 7: Cell Programming 2

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

Page 8: Cell Programming 2

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?

Page 9: Cell Programming 2

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

Page 10: Cell Programming 2

Comandi DMA

Interfaccia di accesso della PPE e della SPE

Comando                     FUNZIONEPPE SPE

GETGETFGETBPUTPUTFPUTB

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

Page 11: Cell Programming 2

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)

Page 12: Cell Programming 2

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

Page 13: Cell Programming 2

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);

}

Page 14: Cell Programming 2

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

Page 15: Cell Programming 2

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

Page 16: Cell Programming 2

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 1control_block cb __attribute__ ((aligned (128))); //struct da inviareint *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;

}

Page 17: Cell Programming 2

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;

}

Page 18: Cell Programming 2

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 risultatifor(i=0;i<BUFF_SIZE;i++) printf("%d ",arr[i]);

Page 19: Cell Programming 2

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_*)

Page 20: Cell Programming 2

Codice PPE#include <libspe2.h>

uint32_t ls_offset; // spiazzamento dei dati nella Local Storevolatile char my_data[BUFF_SIZE] __attribute__ ((aligned(128))); //buffer dei datiint 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();

}

Page 21: Cell Programming 2

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?

Page 22: Cell Programming 2

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

Page 23: Cell Programming 2

Sommario

Accesso alla memoria

Comunicazione inter-processore

− Mailbox

− Segnali

Benchmarking

Page 24: Cell Programming 2

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!

Page 25: Cell Programming 2

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

Page 26: Cell Programming 2

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

Page 27: Cell Programming 2

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)

Page 28: Cell Programming 2

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()

Page 29: Cell Programming 2

Echo, questo sconosciuto...

SPE

uint32_t data;while(spu_stat_in_mbox()<entries){}data = spu_in_mbox_read();spu_write_out_mbox(data)

PPEunsigned 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);

Page 30: Cell Programming 2

??? 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...

Page 31: Cell Programming 2

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 SPEspe_in_mbox_write(spe[i],(uint32_t*)&ctl_addr,2,SPE_MBOX_ALL_BLOCKING);

Page 32: Cell Programming 2

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ù significativiea_l=spu_read_in_mbox(); //32 bit meno significativieff_addr =mfc_hl2ea(ea_h,ea_l); //concatenazione

Page 33: Cell Programming 2

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;

}

Page 34: Cell Programming 2

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;

}

Page 35: Cell Programming 2

Sommario

Accesso alla memoria

Comunicazione inter-processore

− Mailbox

− Segnali

Benchmarking

Page 36: Cell Programming 2

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!

Page 37: Cell Programming 2

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

Page 38: Cell Programming 2

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.

Page 39: Cell Programming 2

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*()

Page 40: Cell Programming 2

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!

Page 41: Cell Programming 2

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.

Page 42: Cell Programming 2

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

Page 43: Cell Programming 2

Sommario

Accesso alla memoria

Comunicazione inter-processore

− Mailbox

− Segnali

Benchmarking

Page 44: Cell Programming 2

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

Page 45: Cell Programming 2

The making of...

Il programma originario è scritto in FORTRAN77

PRIMO PROBLEMA: Riutilizzare le funzioni FORTRAN in un programma scritto in C

Page 46: Cell Programming 2

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!)

Page 47: Cell Programming 2

Riutilizzo delle subroutines FORTRAN

FORTRAN77a = 5b = 3subroutine add(a,b)

a+breturn

end

C che richiama FORTRAN77

int a=5,b=3;add_(&a,&b);

Page 48: Cell Programming 2

Riutilizzo delle subroutines FORTRAN

Cint a=5,b=3;void add_(int *sum,int *a,int *b){

*sum = *a + *b;}

FORTRAN77 che richiama C

call add(sum,a,b)

Page 49: Cell Programming 2

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

Page 50: Cell Programming 2

Riutilizzo dei COMMON BLOCKS

FORTRAN

real*4 alat,dt,dtforce,sigma,sigsq,cutsq1,cutsq2common /blk01/ alat,dt,dtforce,sigma,sigsq,cutsq1,cutsq2

C che richiama FORTRAN

extern struct blk01_;float alat = blk01_.alat;

Tutto finito?

Page 51: Cell Programming 2

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?

Page 52: Cell Programming 2

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?

Page 53: Cell Programming 2

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

Page 54: Cell Programming 2

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...

Page 55: Cell Programming 2

Come agire sul codiceFORTRAN INIZIALEinteger arr(30)subroutine add_arr(a,b)

do i=1,30 arr(i) = a+b;enddoreturn

end

FORTRAN MODIFICATOsubroutine add(a,b,arr,dim_arr)

integer,intent(in)::dim_arrinteger,intent(inout),dimension(dim_arr)::arrdo i=1,30 arr(i) = a+b;enddo

end

Page 56: Cell Programming 2

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!

Page 57: Cell Programming 2

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!

Page 58: Cell Programming 2

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.

Page 59: Cell Programming 2

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

Page 60: Cell Programming 2

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... :'(

Page 61: Cell Programming 2

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...

Page 62: Cell Programming 2

Benchmarking

Page 63: Cell Programming 2

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!

Page 64: Cell Programming 2

Riferimenti

CBEA Handbook

Programming the CBEA, Examples and Best Practises

Page 65: Cell Programming 2

To be continued...

GRAZIE PER LA CORTESE ATTENZIONE