• Non ci sono risultati.

5.4 Adattivit` a in ASSISTANT

5.4.1 Riconfigurazioni non funzionali

Questa modalit`a di riconfigurazione rappresenta una estensione di quella in- trodotta in ASSIST con [79]; con ASSISTANT ci proponiamo di superare le difficolt`a incontrate da tale approccio, e soprattutto offrire al program- matore la possibilit`a di definire comportamenti adattivi personalizzati e non solamente quelli statici presentati in ASSIST.

Con questo termine ci riferiamo ad una modifica del modulo che non tocca la sua struttura parallela n´e il codice utente. Esattamente come in ASSIST, infatti, possiamo modificare la richiesta di risorse di un modulo pa- rallelo cambiandone il grado di parallelismo, oppure migliorare le prestazioni spostandone alcune parti su nodi differenti.

Questo tipo di modifica, ereditata dal precedente ambiente di sviluppo, viene gestita interamente dal supporto e non necessita di lavoro addizio- nale da parte del programmatore durante la definizione dell’applicazione;

possiamo perci`o dire che si tratta di una modifica che si ripercuote solo sull’implementazione del modulo, e non sui livelli superiori.

Modifica del grado di parallelismo

Per chiarire le idee riportiamo un esempio, ragionando su un programma ASSIST. Parliamo di un modulo per la moltiplicazione matrice-vettore, pa- rallelizzato nella forma data-parallel map; il codice di un parmod per tale operazione `e illustrato nel listato 5.1. In questo caso abbiamo definito un partizionamento per righe della matrice (il metodo pi`u comune per questo algoritmo) che ci permette di raggiungere un grado di parallelismo massimo pari al numero di queste. Non abbiamo stencil, ma solo una fase di scatter ed una di gather; il partizionamento inoltre rende la versione parallela ab- bastanza bilanciata. A parte casi particolari, questa applicazione dovrebbe avere una buona scalabilit`a, e garantirci un aumento di prestazioni lineare sul numero di nodi utilizzato.

Assumiamo che per motivi legati all’applicazione questo modulo debba garantire un tempo di servizio minore di una costante, TS. In questo caso il programmatore pu`o chiedere di aumentare il numero di nodi se il tempo di servizio attuale `e maggiore di TS, e diminuire il grado di parallelismo se bastano meno nodi per rimanere sotto la soglia di TS. Questa seconda operazione ha senso nell’ottica di liberare nuove risorse per altre applicazioni (o moduli) nel caso di esecuzione su un cluster, e di risparmio energetico nel caso di esecuzione su un insieme di nodi mobili. In figura 5.2 mostriamo il cambiamento che avverrebbe con questo tipo di riconfigurazione, marcando le modifiche in rosso. Come si pu`o vedere dall’immagine, il programma rimane identico, ma cambia la sua implementazione, che utilizza pi`u nodi dopo la riconfigurazione.

Ovviamente questo ragionamento `e molto “leggero” e non tiene conto delle caratteristiche dell’architettura su cui viene eseguito; ci `e servito solo per mostrare l’utilit`a delle riconfigurazioni non funzionali. In realt`a uno studio pi`u accurato ci porterebbe a stabilire che il tempo impiegato per distribuire i dati aumenta con l’aggiunta di nodi (abbiamo pi`u comunicazioni per la scatterizzazione di A ed inviamo pi`u copie di B) e potrebbe perci`o influire negativamente sulle prestazioni.

Oltretutto fino a questo momento abbiamo pensato all’implementazione classica di ASSIST, che per`o non `e l’unica e potrebbe variare in futuro. Questi problemi si presentano in modo maggiore su parallelizzazioni con stencil, dove le comunicazioni tra i nodi dipendono fortemente dal tipo di ottimizzazioni che il supporto `e riuscito ad introdurre[37].

parmod m a t r i x C a l c ( input stream double A[N ] [ N] , double B [N] output stream double r e s u l t [N ] ) {

topology array [ i :N] Pv ; stream double o u t m a t r i x ; do input section { guard1 : on , , A && B { d i s t r i b u t i o n A[ ∗ i 0 ] [ ] s c a t t e r to Pv [ i 0 ] ; d i s t r i b u t i o n B broadcast to Pv ; } } while ( true ) v i r t u a l p r o c e s s o r s { e l a b o r a z i o n e ( in guard1 out o u t m a t r i x ) { VP i = 1 . .N { Fmul ( in A[ i ] [ ] , B [ ] out o u t m a t r i x ) ; } } } output section { c o l l e c t s o u t m a t r i x from ALL Pv [ i ] { i n t elem ; double r i s [N ] ;

AST FOR EACH( elem ) { r i s [ i ]= elem ; } a s s i s t o u t ( r e s u l t , r i s ) ;

}<>; } }

proc Fmul ( in double A[N] , double B [N] out double C) $c++{ double r =0;

f o r ( i n t k =0; k<N; ++k ) { r += A[ k ] ∗B [ k ] ; } C = r ; } c++$

Listato 5.1: Modulo ASSIST per la moltiplicazione matrice-vettore parallela

Riconfigurazione non funzionale - Cambio del grado di parallelismo

OSM ISM VPM1 VPM2 OSM ISM VPM3 VPM1 VPM2 A 1 6 VP1 VP2 VP3 VP4 VP5 VP6 B 1 6 B 1 6 A 1 3 2 C 1 3 2 B 1 6 A 4 6 5 C 4 6 5 C 1 6 VP1 VP3 VP2 VP4 VP6 VP5 A 1 6 B 1 6 B 1 6 A 1 2 C 1 2 B 1 6 A 6 5 C 6 5 C 1 6 A 3 4 B 1 6 C 3 4

Figura 5.2: Modifica del grado di parallelismo del modulo sopra descritto: un esempio di riconfigurazione non funzionale.

Per questi motivi il programmatore ASSISTANT non pu`o essere a cono- scenza dei modelli dei costo della propria applicazione, in quanto sono stret- tamente dipendenti dall’architettura di esecuzione e dall’implementazione fornita dal supporto.

Infine, se anche i modelli di costo fossero fissati, il programmatore diffi- cilmente potrebbe conoscerli a fondo, e rischierebbe di utilizzarli nel modo sbagliato.

In questo scenario le riconfigurazioni non funzionali perderebbero gran parte del proprio significato, in quanto difficilmente si potrebbe decidere se conviene riconfigurarsi ed in che modo.

In ASSISTANT vogliamo inserire meccanismi per offrire nativamente al programmatore questi modelli di costo, per permettergli di valutare se la riconfigurazione pu`o rivelarsi valida oppure no. In questa ottica pensiamo di offrire un insieme di funzioni, che forniscano stime del comportamento del modulo al variare del grado di parallelismo. Come detto prima, per ora conosciamo solo stime legate alla performance, ma contiamo di introdurne altre col tempo.

Modifica dell’allocazione del modulo sui nodi disponibili

Proponiamo ora un secondo esempio importante di riconfigurazione funziona- le, sempre a partire dall’esempio del listato 5.1. Pensiamo ad una esecuzione su un ambiente molto dinamico, dove nuovi nodi entrano regolarmente nel sistema. In questo caso, anche senza modificare il grado di parallelismo, po- tremmo essere interessati a “spostare” parte della computazione su uno dei nuovi nodi del sistema. Questo per motivi prestazionali (il nuovo nodo `e pi`u veloce) o anche per problematiche di tolleranza alle disconnessioni (ad esem- pio, la probabilit`a che un nodo si disconnetta potrebbe essere proporzionale al suo tempo di rimanenza nella rete).

Un altro esempio importante si pu`o trovare durante l’esecuzione su di- spositivi a batteria (portatili,pda,etc): se uno dei nodi utilizzati raggiunge un livello di carica critico, si dovrebbe spostare la computazione su uno li- bero, anche se con prestazioni inferiori. Abbiamo riportato questo esempio nella figura 5.3, dove nella configurazione iniziale il sistema aveva scelto due laptop per la parte computazionalmente intensiva del modulo (la sezione vir- tual processors), e solo le parti di distribuzione e collezione venivano eseguite su terminali pi`u lenti (i PDA). Quando uno dei portatili raggiunge un livello di batteria critico, l’allocazione delle risorse viene modificata, spostando i processi allocati nel laptop su un PDA precedentemente in standby.

. . .

VPM2 VP4 VP6 VP5 VPM1 VP1 VP3 VP2 ISM OSM

. . .

VPM2 VP4 VP6 VP5 VPM1 VP1 VP3 VP2 ISM OSM

Riconfigurazione non funzionale - Riallocazione delle risorse

Figura 5.3: Un secondo esempio di riconfigurazione non funzionale: modifica dell’allocazione delle risorse in caso di livello di batteria critico.

Questo secondo esempio ci pone un problema non ancora considerato, ov- vero la conoscenza dei dispositivi presenti nell’ambiente. Nell’esempio di prima, infatti, il programmatore si limitava a chiedere la modifica del grado di parallelismo al sistema; qui, invece, dovremmo avere conoscenza di tut- ti i nodi utilizzati dal modulo stesso e di eventuali nodi “liberi”. Questo complicherebbe notevolmente la modellazione di una richiesta di riconfigu- razione, richiederebbe nuovamente al programmatore di conoscere dettagli dell’implementazione e sarebbe comunque difficile da gestire.

Per questo motivo, per il momento non pensiamo di inserire questa tipolo- gia in ASSISTANT, ma questo non vuol dire che il problema verr`a ignorato per sempre: a livello di supporto inseriremo i meccanismi per permettere anche questo tipo di riconfigurazione1, e quando riusciremo a definire mec-

canismi automatici per la decisione delle riconfigurazioni non funzionali, in questi inseriremo anche la logica per gestire casi come quello dell’esempio.

Un lettore attento noter`a che, in fondo, anche nella modifica del grado di parallelismo emergono problemi di allocazione su nuovi nodi; in quel caso abbiamo deciso di lasciar scegliere al supporto quali rimuovere e/o inserire, anticipando quindi alcuni meccanismi di scelta dei nodi che in futuro verranno formalizzati in modo migliore.