Appunt i di Ca l c o l a t o r i El e t t r o ni c i C a p i t o l o 1 0 – P a r a l l e l i s mo n e i c a l c o l a t o r i D IREZIONI FUTURE ............................................................................................................. 2 Introduzione .................................................................................................................. 2 Tassonomia dei calcolatori secondo Flynn ....................................................................... 2 Calcolatori SIMD ........................................................................................................... 3 Calcolatori MISD ........................................................................................................... 5 Calcolatori MIMD.......................................................................................................... 5 Terminologia per le macchine MIMD ........................................................................... 6 Architettura MIMD a scambio di messaggi.................................................................... 7 Architettura MIMD a memoria condivisa ...................................................................... 8 Problema della coerenza delle cache.......................................................................... 9 Prestazioni e problemi delle macchine MIMD ............................................................. 10 Processori per applicazioni speciali: DSP...................................................................... 12 APPUNTI VARI SUI SISTEMI PARALLELI ............................................................................... 13 Limiti della classificazione di Flynn e nuova classificazione............................................ 13 Architetture sincrone ................................................................................................. 13 Architetture MIMD ................................................................................................... 14 Paradigmi MIMD ...................................................................................................... 15 Confronto tre architetture seriali, pipelined e parallele .................................................. 15 Pipelining................................................................................................................. 16 Parallelismi .............................................................................................................. 18 Valutazione delle prestazioni ........................................................................................ 19 Strutture di interconnessione......................................................................................... 20 Banda passante di un sistema di calcolo ........................................................................ 20 Legge di Amdahl applicata ad una macchina con N processori ........................................ 21 Legge di Braunl per architetture SIMD .......................................................................... 23 Tempo di comunicazione inter-processor........................................................................ 25 Legge di Amdahl modificata .......................................................................................... 25 Appunti di “Calcolatori Elettronici” – Capitolo 10 D Diirreezziio on nii ffu uttu urree Introduzione In questo capitolo vogliamo abbandonare la prospettiva sostanzialmente “conservativa” seguita nei precedenti capitoli, dando invece dei cenni al futuro dell’architettura dei calcolatori. In generale, possiamo allora subito dire che gli obbiettivi dei progetti innovativi sono essenzialmente due, alternativi tra loro: • riduzione del rapporto costo/prestazioni; • raggiungimento di un’alta scalabilità ( 1) affiancata da un soddisfacente rapporto costo/prestazioni. Abbiamo ampiamente visto che le CPU con parallelismo a livello di istruzioni sfruttano il parallelismo intrinseco di un programma, ossia quello esistente tra le singole istruzioni. Si può però facilmente verificare che il parallelismo di tale tipo è relativamente limitato, salvo applicazioni molto particolari: risulta infatti difficile raggiungere una buona efficienza di uso al di sopra di 8 unità funzionali parallele all’interno della singola CPU. A questo, si aggiunge il fatto che esistono applicazioni nelle quali è molto difficile, se non impossibile, individuare parallelismo a livello di istruzione: i sistemi operativi sono un classico esempio a questo proposito. L’alternativa emersa da alcuni decenni è quella dei cosiddetti sistemi paralleli, nei quali cioè il parallelismo sfruttato è quello a livello di sequenza di istruzioni o di processo: l’architettura, in questi sistemi, è costituita da un certo numero di unità di elaborazione, dotate ognuna di proprie risorse e che interagiscono l'una con l’altra in modo opportuno. Il concetto alla base dei sistemi paralleli è quello per cui, eseguendo simultaneamente più passi di uno stesso problema, si può completare il lavoro molto più velocemente . Tassonomia dei calcolatori secondo Flynn Diversi anni fa, Flynn propose un semplice metodo di classificazione dei calcolatori, basato sul parallelismo del flusso di istruzioni e del flusso di dati ad esso connesso: • con flusso di istruzioni intendiamo la sequenza di istruzione eseguite da una unità di elaborazione (un processore); • con flusso di dati intendiamo invece la sequenza di operandi manipolato dalla suddetta unità di elaborazione. Flynn propose le seguenti 4 categorie (classificazione di Flynn): 1 La scalabilità è sostanzialmente la possibilità di ripartire lo svolgimento di un dato compito tra più unità funzionali, al fine di sveltire lo svolgimento stesso. Autore: Sandro Petrizzelli 2 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo • macchine SISD (Single Instruction Single Data): flusso di istruzioni unico e flusso di dati unico; sono le classiche macchine monoprocessore di tipo sequenziale; • macchine SIMD (Single Instruction Multiple Data): esiste un unico flusso di istruzioni, che però viene applicato a più flussi di dati; risono dunque più processori che eseguono simultaneamente la stessa istruzione su dati diversi ( 2); • macchine MISD (Multiple Instruction Singole Data): esiste un unico flusso di dati, cui però vengono applicati diversi flussi di istruzioni; in pratica, più processori eseguono in maniera autonoma istruzioni diverse sullo stesso flusso di dati; • macchine MIMD (Multiple Instruction Multiple Data): in quest’ultimo caso, più processori eseguono in maniera autonomia istruzioni diverse su dati diversi. Nonostante questa classificazione appaia un po’ grossolana (molte macchine sono infatti un ibrido tra varie categorie) e ormai decisamente superata, essa è indubbiamente semplice e facile da comprendere e, in ogni caso, fornisce una prima approssimazione accettabile della realtà attuale. La macchina tradizionale di Von Neumann è ovviamente di tipo SISD: essa ha un unico flusso di istruzioni (cioè un unico programma) eseguito da una sola CPU ed ha poi un’unica memoria che contiene i dati su cui opera il flusso di istruzioni; la prima istruzione viene presa dalla memoria e poi eseguita, dopodiché si passa al prelievo ed alla esecuzione dell’istruzione successiva e così via fino al termine del programma. Le altre tre tipologie, invece, fanno tutte riferimento a macchine che si definiscono parallele, dove il parallelismo riguarda appunto il flusso di istruzioni o il flusso di dati o entrambi. Nelle classi SIMD e MIMD ricade la quasi totalità di macchine parallele realmente costruite. Le macchine MISD, invece, sono sostanzialmente un caso particolare della classe MIMD. Calcolatori SIMD Come anticipato nel precedente paragrafo, una macchina SIMD è caratterizzata dal fatto che il flusso di istruzioni è singolo, ma opera su più flussi di dati. In questo caso, si può pensare ad una sola unità di controllo che acceda ad una sola memoria istruzioni ed invii, ciclo per ciclo, la stessa istruzione da eseguire a un insieme di unità di esecuzione, che accedono invece ognuna a una propria area della memoria dati (o, alternativamente, che sono dotate ognuna di una propria memoria dati opportunamente precaricata) ( 3). Architetture di questo tipo possono essere ricondotte nello schema più semplice alla struttura seguente: 2 Vedremo più avanti che macchine di questo tipo sono anche dette calcolatori matriciali o array computers. I processori vettoriali che abbiamo esaminato nel capitolo 7 potrebbero sembrare delle macchine SIMD, ma in realtà non lo sono: infatti, abbiamo visto che le istruzioni vettoriali operano su più dati, usando una sola unità funzionale con un parallelismo di tipo pipeline; quindi, al contrario di quanto avviene nelle macchine SIMD, una singola istruzione vettoriale non attiva molte unità funzionali; invece, una macchina SIMD effettiva potrebbe ad esempio avere 64 dati che vanno simultaneamente a 64 unità ALU per formare 64 somme nello stesso ciclo di clock. 3 aggiornamento: 15 luglio 2001 3 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 Memoria istruzioni e unità di controllo Unità di elaborazione Unità di elaborazione ...... Unità di elaborazione Memoria Dati Le varie unità di elaborazione possono naturalmente essere organizzate secondo schemi più complessi ed eventualmente scambiarsi informazioni. In ogni caso, in una struttura di questo tipo, i singoli processori possono anche essere dispositivi molto semplici (al limite, unità aritmetiche dotate di registri di ingresso e di uscita) come anche architetture dedicate a particolari computazioni. La sincronizzazione fra i diversi nodi è rigida, dato che tutti, almeno in linea di principio, devono eseguire l'identica istruzione, con la stessa latenza, sui propri dati. Il vantaggio principale delle macchine SIMD è dunque quello per cui tutte le unità funzionali per l’esecuzione parallela sono sincronizzate e rispondono ad una sola istruzione , proveniente da un unico registro PC ( 4). Ci sono poi altri vantaggi: • in primo luogo, si può ridurre il costo dell’unità di controllo affiancandola a decine di unità di esecuzione; • in secondo luogo, di recente ci si è resi conto che si può anche adottare una dimensione ridotta della memoria di programma: infatti, le macchine SIMD necessitano di una sola copia del programma da eseguire, al contrario per esempio delle macchine MIMD che richiedono una copia per ogni processore. In questo modo, anche in presenza di un elevato numero di unità funzionali, il costo della memoria di programma risulta minore per le architetture della classe SIMD rispetto alle altre. I calcolatori SIMD, al pari delle macchine vettoriali, presentano istruzioni “compatibili” sia con la classe SIMD (ovviamente) sia anche con la classe SISD: per esempio, operazioni come il calcolo di diramazioni o di indirizzi non richiedono alcun parallelismo e sono quindi eseguite da un host computer di tipo SISD. Le istruzioni SIMD sono invece trasmesse a tutte le unità di esecuzione, ognuna delle quali è provvista del suo insieme di registri. Come le macchine vettoriali, anche le macchine SIMD hanno istruzioni che disabilitano le singole unità di esecuzione. Al contrario delle macchine vettoriali, invece, l’elevato parallelismo delle macchine SIMD dipende strettamente dalle reti di interconnessione, ossia da tutto quanto (hardware e software) viene usato per lo scambio di dati tra i vari elementi processanti. 4 In questo senso, dal punto di vista del programmatore, il comportamento della macchina è molto simile a quello di una classica macchina SISD (monoprocessore). Autore: Sandro Petrizzelli 4 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo Le architetture SIMD raggiungono le loro massime prestazioni quando le istruzioni vettoriali vengono utilizzate in pieno, ad esempio quando gli array vengono scanditi all’interno di cicli FOR. Questo per dire che, per ottenere il massimo parallelismo, occorre una “grande quantità di dati”, intesa naturalmente come “parallelismo dei dati”. Il limite principale di una macchina SIMD emerge invece quando essa deve eseguire comandi in cui ogni singola unità di esecuzione deve svolgere operazioni diverse su dati diversi. Il progetto di una macchina SIMD richiede di porre attenzione a due importanti fattori ed al loro rapporto: l’efficienza di utilizzo del singolo processore ed il numero di processori. La tendenza attuale delle macchine SIMD sembra privilegiare un elevato grado di parallelismo rispetto all’elevato rendimento del singolo processore. Architetture di tipo SIMD sono state ampiamente studiate, in particolare per l'elaborazione di segnale e per quella delle immagini: per esempio, è interessante ricordare che l'estensione MMX dell'architettura Pentium, progettata esplicitamente per elaborazione di immagine, è riconducibile a uno schema SIMD. Calcolatori MISD Non è del tutto intuitivo immaginare una macchina MISD, in cui cioè più flussi di istruzioni operano contemporaneamente su un unico flusso di dati, in quanto risulta molto più semplice pensare a flussi multipli ognuno associato ad un proprio flusso di dati (che poi è la definizione di macchine MIMD). Ad ogni modo, due esempi di macchine MISD potrebbero in qualche modo essere le macchine superscalari e le architetture VLIW descritte nel paragrafo 6, che però presentano una differenza non irrilevante con le macchine MISD propriamente dette: hanno un unico registro PC, mentre invece le macchine MISD prevedono un registro PC per ciascun flusso. Calcolatori MIMD Potremmo affermare che le macchine MIMD sono le macchine parallele per eccellenza, dato che presuppongono il parallelismo sia a livello di flusso di istruzioni sia a livello di flusso di dati . In effetti, fin dalle prime realizzazioni di calcolatori, i progettisti si sono sempre sforzati di ottenere macchine di questo tipo, in cui cioè la “potenza” in generale fosse ottenuta semplicemente connettendo molti calcolatori più piccoli. Con una simile architettura, l’utente dispone, in modo ordinato, di tante CPU quante se ne può permettere ed ottiene un rendimento di quantità proporzionata ( 5). Le architetture MIMD hanno almeno altri due vantaggi, oltre le prestazioni particolarmente elevate: un rendimento assoluto più alto rispetto al più potente monoprocessore ed anche un più alto rapporto affidabilità/disponibilità, ottenuto proprio sfruttando la ridondanza di processori (se un processore si guasta, ce ne sarà un altro pronto a lavorare per lui). 5 Vedremo comunque tra poco che l’aumento di prestazioni non è lineare come l’aumento delle CPU utilizzate. aggiornamento: 15 luglio 2001 5 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 T Teerrm miin noollooggiiaa p peerr llee m maacccch hiin nee M MIIM MD D Prima di fornire qualche dettaglio maggiore sulle macchine MIMD, risulta opportuno introdurre un po’ di terminologia relativa a questo tipo di macchine. Una importante suddivisione delle macchine MIMD è relativa alle modalità con cui viene ripartita l’ “informazione”: • nei processori a memoria condivisa, esiste una zona di memoria cui possono accedere tutti i processori ( 6); i processori comunicano attraverso variabili allocate in tale zona di memoria condivisa. Per coordinare i vari processi, sono disponibili delle primitive di sincronizzazione; • in alternativa, per la condivisione dei dati si può pensare di mettere in comunicazione i vari processi tramite uno scambio di messaggi. Sulla base di questa suddivisione, Bell ha suggerito l’uso delle seguente terminologia: • multiprocessore è una macchina MIMD in cui la comunicazione avviene tramite memoria condivisa; • multicalcolatore è invece una macchina MIMD in cui la comunicazione avviene solo tramite il passaggio di messaggi espliciti. In effetti, ci sarebbe da precisare una cosa: in presenza di memoria condivisa, la si potrebbe comunque usare per lo scambio di messaggi, per cui un multiprocessore potrebbe essere in grado di eseguire programmi basati invece sullo scambio di messaggi (e quindi destinati ai multicalcolatori); dualmente, un multicalcolatore potrebbe simulare, proprio tramite i messaggi, la presenza di una memoria condivisa, in modo da eseguire programmi che richiedono l’uso della memoria condivisa. Ecco allora che la distinzione proposta da Bell è specificamente basata sull’hardware a disposizione della macchina e sul modello di esecuzione del programma e non sul software che dovrebbe essere eseguito. Ci sono sostenitori sia del multiprocessore sia del multicalcolatore: i primi mettono in discussione la programmabilità dei multicalcolatori (data la necessità e la difficoltà di progettare le primitive per lo scambio dei messaggi), mentre invece i secondi mettono in discussione la scalabilità dei multiprocessori (data appunto la presenza di un’unica memoria condivisa, che potrebbe essere limitante delle prestazioni). Ad ogni modo, analizziamo con maggiore dettaglio le due soluzioni: • 6 architetture a scambio di messaggi: o il parallelismo è fra processi: tipicamente, si alloca un processo su ogni nodo, dove per nodo intendiamo un singolo processore e la sua memoria; o i processi comunicano solo scambiandosi messaggi, mediante primitive send (accompagnate dall’identificatore del destinatario, da un'etichetta e dal messaggio vero e proprio) e receive (accompagnata da etichetta e messaggio); o i processi si sincronizzano “bloccandosi” sui messaggi: il processo che ha inviato il messaggio si blocca in attesa della risposta. Inoltre, essi si I multiprocessori dotati di cache coerenti sono tipiche macchine a memoria condivisa. Autore: Sandro Petrizzelli 6 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo bloccano ponendo delle “barriere” che non possono essere superate fino a quando lo scambio non è andato a buon fine. • architetture a memoria condivisa: o sono i cosiddetti threads (cioè sottoprocessi che condividono lo stesso spazio di indirizzamento) ad essere allocati sui diversi nodi (CPU); o i threads comunicano attraverso uno spazio di memoria condiviso (mediante normali istruzioni load e store del tipo visto per il DLX); o i threads si sincronizzano tra loro, mediante operazioni "atomiche" (non divisibili) sulla memoria o mediante barriere. In una macchina MIMD, in generale, l’applicazione da eseguire deve essere innanzitutto partizionata (in processi o in threads), in modo da essere allocata sui diversi nodi, e le diverse unità di programma devono poi essere ordinate nel tempo (schedulazione) in modo da garantire che le dipendenze di dati e di controllo vengano rispettate. A meessssaaggggii dii m mbbiioo d D aa ssccaam MD MIIM urraa M hiitteettttu Arrcch Lo schema generale di una simile architettura (detta anche a memoria distribuita) è il seguente: nodo CPU CPU CPU ................... Memoria Memoria Memoria Rete di Interconnessione I vari calcolatori (nodi) sono collegati tramite una rete di interconnessione; ogni nodo viene dotato di una interfaccia veloce alla suddetta rete, in modo da implementare in modo efficiente le primitive di send e di riceive, nonché le barriere di cui si è parlato. A parte questo, sotto ogni altro aspetto il sistema è indistinguibile da un normale sistema monoprocessore. In queste architetture, i collegamenti tra le unità di elaborazione sono tali che ognuna di essere abbia il controllo esclusivo di una certa porzione di memoria . Ci sono in proposito vari schemi di connessione: ad anello, a maglia, ad albero binario. Una soluzione particolare è quella che prevede una topologia ad ipercubo: le unità di elaborazione sono collegate tra loro come se si trovassero ai vertici di un cubo multidimensionale. Il vantaggio è che, pur senza realizzare un numero elevato di collegamenti, ogni unità di elaborazione può inviare messaggi ad una qualsiasi altra tramite un percorso comunque breve. aggiornamento: 15 luglio 2001 7 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 A Arrcch hiitteettttu urraa M MIIM MD D aa m meem moorriiaa ccoon nd diivviissaa Lo schema generale di una simile architettura è il seguente: CPU CPU ......................... CPU Rete di Interconnessione Memoria Condivisa I vari processori condividono in questo caso un unico spazio di indirizzamento ; le comunicazioni sono implicite, in quanto avvengono mediante letture e scritture in posizioni di memoria condivise. La sincronizzazione avviene ancora mediante la condivisione della memoria e mediante le barriere. All’aumentare del numero di processori, la memoria condivisa diviene il collo di bottiglia di tutto il sistema; al contrario, nel caso delle architetture con scambio di messaggi, questo problema non si poneva, ma il collo di bottiglia era comunque nella struttura di interconnessione. Per aggirare il problema del collo di bottiglia sulla memoria, in molti casi questa architettura concettuale viene in realtà implementata con un multiprocessore in cui lo spazio di indirizzamento viene distribuito sulle memorie locali dei vari nodi o in cui comunque ogni nodo è dotato di una cache locale mentre è la RAM ad essere condivisa. Si realizza perciò una architettura del tipo seguente: nodo CPU CPU CPU ................... Cache Cache Cache Rete di Interconnessione Memoria Condivisa Autore: Sandro Petrizzelli 8 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo Uno svantaggio di questo sistema si ha quando si decide che ogni unità di elaborazione può accedere a tutte le unità di memoria globali e comuni: servono infatti molti collegamenti (si parla di schema a connessione diretta). Una alternativa sarebbe l’uso di un bus lungo il quale le unità di elaborazione inviano richieste ai banchi di memoria, i quali in risposta inviano i dati: tuttavia, sarebbe nuovamente il bus ad essere troppo affollato. Una ulteriore possibilità è la cosiddetta rete a OMEGA: le unità di elaborazione sono connesse ai banchi di memoria tramite una serie di commutatori, ognuno dei quali ha due linee di ingresso e due di uscita; in tal modo, ogni unità di elaborazione può accedere a tutti i banchi di memoria, ma non è necessario avere tante linee di comunicazione come nello schema a connessione diretta, in quanto ci pensano i commutatori ad instaurare il “percorso” richiesto dal collegamento. E’ chiaro che il vantaggio è tanto maggiore quanto maggiore è il numero di unità di elaborazione e di banchi di memoria. Al contrario, c’è lo svantaggio per cui ogni messaggio deve passare attraverso molti commutatori prima di giungere a destinazione. Problema della coerenza delle cache Una soluzione in cui i nodi sono dotati ciascuna di una propria cache fa nascere un problema particolare: è il cosiddetto problema della coerenza della cache. Ad esempio, un processore potrebbe leggere dati “vecchi” nella propria cache, mentre quelli nella memoria condivisa sono stati aggiornati da un altro processore; oppure, un processore può leggere dati vecchi nella memoria condivisa invece di quelli aggiornati nella cache di un altro professore. Si può pensare a varie soluzioni per questo problema: • la soluzione più banale consiste nell'evitare di portare in cache i dati condivisi; • una soluzione più efficiente richiede invece l'introduzione di un protocollo per la coerenza delle cache: tale protocollo deve tener traccia delle varie copie dei dati condivisi e deve informare i nodi (con un aggiornamento o semplicemente “invalidando” i dati vecchi) quando un nodo compie un’operazione di scrittura. A proposito di questa seconda soluzione, si può pensare a due diverse implementazioni effettive: • la prima è quella di un protocollo snoopy (cioè “curioso”): in pratica, una scrittura viene inviata in broadcast a tutti i nodi, il che presuppone ovviamente che ogni nodo “resti in ascolto” delle eventuali scritture e stabilisca se deve aggiornare o meno la propria cache; • la seconda implementazione è quella dei cosiddetti protocolli directorybased, nei quali una transazione di scrittura viene inviata solamente a quei nodi di cui è noto che possiedono una copia del dato da aggiornare. In questo caso, deve esserci da qualche parte una specie di tabella consultabile da ciascun nodo, in cui siano registrati i nodi che possiedono una copia del dato che si sta per scrivere; così facendo, il nodo che vuole effettuare la scrittura può informare solo i nodi interessati e inviare solo a loro i nuovi dati. aggiornamento: 15 luglio 2001 9 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 E evidente che la prima soluzione è più semplice, ma comporta un traffico molto più elevato sul bus, data la necessità del broadcast; essa si adatta perciò soprattutto a quelle architetture in cui il collegamento tra i nodi viene effettuato mediante un bus condiviso. Ad ogni modo, ad prescindere dalla soluzione scelta, il nodo può poi decidere se aggiornare la copia invalidata oppure no: ad esempio, se i dati coinvolti non sono più necessari, non è necessario perdere tempo ad aggiornarli. Resta poi insoluta un’ultima questione: se aggiornare anche la memoria primaria quando si aggiornano le cache, oppure considerare corretti solo i dati contenuti in queste ultime. P Prreessttaazziioon nii ee p prroobblleem mii d deellllee m maacccch hiin nee M MIIM MD D Rispetto ad una tradizionale macchina SISD, le macchine MIMD offrono prestazioni decisamente migliori per l’implementazione di sistemi time sharing (cioè a condivisione di tempo): a parità di carico di lavoro, in una macchina MIMD si possono infatti eseguire in parallelo più compiti tra loro indipendenti, nonostante il tempo di CPU di ciascun compito rimanga invariato rispetto alle macchine SISD. Il risultato è dunque un maggiore throughput, mentre la latenza dei singoli programmi rimane invariata ( 7). Proprio per questo motivo, attualmente le macchine MIMD rappresentano una fetta di mercato significativa, che si aggiudica la maggior parte del settore dei mainframe e in pratica tutto il settore dei supercalcolatori. Ad ogni modo, non bisogna pensare, relativamente alle macchine MIMD, che sia tutto “rose e fiori”. Infatti, uno dei principali punti deboli per queste macchine è il numero ancora troppo esiguo di applicazioni destinate specificamente a questo tipo di macchine e quindi in grado di sfruttarne le potenzialità: la maggior parte delle applicazioni non sono state scritte o adattate (rispetto alle versioni originali per le macchine SISD) per sfruttare tutti i processori presenti e ridurre così il throughput, il che inficia l’opportunità di acquistare questo tipo di macchine. Viene ovviamente da chiedersi il perché di questa carenza di applicazioni: la risposta sta proprio nella difficoltà di scrivere simili applicazioni. Per comprendere a pieno il concetto, facciamo una analogia, confrontando il tempo necessario affinché un dato lavoro venga svolto da un’unica persona o da un gruppo di persone: almeno a livello teorico, se il gruppo è formato da N persone, il lavoro dovrebbe essere completato in un tempo N volte inferiore rispetto a quello impiegato da un singolo individuo (per cui si avrebbe una accelerazione pari idealmente ad N); in realtà, non sarà mai così , in quanto è necessario quanto meno che le varie persone del gruppo comunichino tra loro man mano che svolgono i propri compiti; la difficoltà di tale comunicazione è minima se le persone sono 2 o 3, ma se diventano 1000 o 10000, allora è evidentemente che le cose si complicano non poco. Più avanti nel capitolo faremo una analisi più quantitativa di questo discorso. Un’altra difficoltà per la scrittura di applicazioni per macchine MIMD è il grado di conoscenza dell’hardware richiesta al programmatore: se quest’ultimo deve scrivere una applicazione (in un linguaggio ad alto livello) per una macchina SISD, può tranquillamente disinteressarsi dell’hardware della macchina, in quanto sarà poi compito del compilatore ottimizzare il codice macchina in funzione di tale hardware; al contrario, per una macchina multiprocessore, il programmatore scriverà 7 Accade cioè una cosa analoga a quella che riguarda il singolo programma eseguito su una macchina SISD dotata di pipeline: la latenza delle singole istruzioni rimane invariata, ma il CPI viene molto ridotto e quindi l throughput finale è maggiore. Autore: Sandro Petrizzelli 10 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo programmi tanto più efficienti e scalabili quanto maggiore è la sua conoscenza sia dell’hardware sia dell’organizzazione della macchina. Tra l’altro, quest’ultima considerazione rende i programmi paralleli poco portabili, proprio perché nascono specificamente per una macchina e difficilmente si adeguano ad un’altra macchina (o quanto meno non riescono a sfruttarne a pieno le potenzialità). L’ultima limitazione per la scrittura di applicazioni parallele proviene direttamente dalla legge di Amdahl: infatti, questa legge evidenzia che anche le parti meno importanti di un programma devono essere parallelizzate se si vuole raggiungere il pieno potenziale, in quanto tutte le eventuali migliorie apportate alla macchine e/o al programma forniscono incrementi di prestazioni tanto più pronunciati quanto maggiore è la frazione di tempo in cui possono essere impiegati. Per comprendere bene questo aspetto, possiamo fare un esempio molto semplice: supponiamo di voler eseguire un programma su un macchina dotata di 100 processori e di voler ottenere un incremento di prestazioni equivalente rispetto ad una macchina SISD; ci chiediamo quale porzione della computazione originale può rimanere sequenziale e quale invece debba essere parallelizzata. Ci basta applicare la legge di Amdahl, che riportiamo qui di seguito nella sua espressione riassuntiva: Accelerazione = In questa espressione, il 1 (1 - Frazione termine Frazione ) + Accelerazi one migliorata migliorata “ Frazione migliorata ” migliorata indica la frazione di computazione che deve risultare migliorata (ossia, nel nostro caso, la frazione di codice che deve essere parallelizzata), mentre invece il termine “ Accelerazi one migliorata ” è il miglioramento di prestazioni ottenibile nel caso ideale in cui tutta la computazione venisse ottimizzata. Nel nostro caso, quest’ultima quantità vale 100, dato che usiamo 100 processori al posto di uno solo, per cui cominciamo a scrivere che Accelerazione = 1 (1 - Frazione ) + Frazione 100 migliorata migliorata Dobbiamo ora inserire l’obbiettivo che ci siamo preposti, ossia un miglioramento complessivo delle prestazioni pari a 100: 100 = 1 (1 - Frazione migliorata )+ Frazione migliorata 100 Questa equazione va risolta nell’unica incognita rimasta ed è intuitivo il risultato finale: Frazione migliorata = 1 , il che significa che, per ottenere un incremento lineare con 100 processori, TUTTA la computazione originale deve essere parallelizzata. Se per esempio avessimo voluto una accelerazione complessiva pari a 99, sarebbe risultato Frazione migliorata = 0.99999 , ossia solo una parte pari a 0.0001 di computazione originale sarebbe potuta rimanere sequenziale. Visto in un’altra prospettiva, quest’ultimo calcolo mostra che anche una piccolissima porzione di codice non parallelizzato (0.0001, cioè lo 0.01%) può determinare una riduzione dell’accelerazione aggiornamento: 15 luglio 2001 11 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 complessiva rispetto a quella ideale desiderata. Anche di questo aspetto ci occuperemo comunque in seguito nel capitolo. Processori Processor i per applicazioni speciali: DSP Quanto detto nei paragrafi precedenti illustra dunque la grande attenzione che i progettisti devono rivolgere e rivolgono effettivamente al parallelismo ottenibile nei calcolatori. Un'altra questione ampiamente approfondita da progettisti è quella dei calcolatori per applicazioni speciali: grazie all’ausilio di sempre più sofisticati programmi di CAD (Computer Aided Design) e all’incremento della capacità dei singoli chip, si è infatti presentata l’opportunità di produrre, rapidamente ed a prezzi ridotti, circuiti volti a risolvere in maniera efficiente compiti particolari. Esempi classici sono riscontrabili nei dispositivi per il riconoscimento vocale in tempo reale e per l’elaborazione di immagini. I dispositivi per uso speciale (detti spesso coprocessori) agiscono generalmente in collaborazione con la CPU. Uno dei filoni attualmente più diffusi è quello dei dispositivi per l’elaborazione dei segnali digitali (DSP, Digital Signal Processing): questi dispositivi non rispondono ai modelli di calcolo tradizionali, ma sembrano piuttosto macchine microprogrammate orizzontali ( 8) o al più macchine VLIW ( 9). Essi tendono a risolvere problemi in tempo reale partendo essenzialmente da un flusso di dati (in questo caso bit) di tipo continuo. 8 Ne abbiamo parlato nel capitolo 5: sono macchine in cui si predilige l’uso di poche microistruzioni ma di grande lunghezza al posto di molte microistruzioni di piccola lunghezza. 9 Ne abbiamo parlato nel capitolo 6: sono macchine in cui una singola istruzione corrisponde all’esecuzione di più operazioni. Autore: Sandro Petrizzelli 12 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo A Ap pp pu un nttii vvaarrii ssu uii ssiisstteem mii p paarraalllleellii Limiti della classificazione di Flynn e nuova classificazione La classificazione di Flynn (che suddivide le architetture dei calcolatori in SISD, SIMD, MISD e MIMD), per quanto molto semplice, appare oggi insufficiente per classificare i calcolatori presenti sul mercato. Ad esempio, i cosiddetti processori vettoriali in pipeline possono essere considerati come macchine parallele, ma difficilmente li si può classificare secondo Flynn: infatti, non sono macchine di tipo SIMD in quanto non hanno più processori che eseguono la stessa istruzione, né possono essere viste come macchine MIMD in quanto non sono asincrone. Una classificazione senz’altro più attuabile, al giorno d’oggi, è quella che individua le seguenti categorie: • architetture sincrone: o processori vettoriali in pipeline; o processori SIMD del tipo processor array; o processori SIMD del tipo associative memory; o processori sistolici; • architetture MIMD: o architetture a memoria distribuita; o architetture a memoria condivisa; • paradigmi MIMD: o calcolatori ibridi MIMD/SIMD; o calcolatori data flow; o calcolatori di riduzione; o calcolatori wave front. A Arrcch hiitteettttu urree ssiin nccrroon nee I processori vettoriali in pipeline (come il DLXV) sono caratterizzati dall’avere più unità funzionali in pipeline, che effettuano in parallelo operazioni aritmetiche e logiche sia su vettori sia su scalari. Il parallelismo si manifesta nella sovrapposizione di fasi diverse di una stessa istruzione su dati diversi, come in una catena di montaggio in cui vengono lavorati simultaneamente più pezzi. In strutture di questo tipo, il tempo di latenza è definito come il tempo che intercorre tra l’inizio dell’elaborazione e l’apparire del primo risultato. Una struttura con elevato tempo di latenza può risultare comunque efficiente solo quando vi è un gran numero di calcoli identici da eseguire, in quanto in questo modo nessuna unità funzionale risulta inattiva. aggiornamento: 15 luglio 2001 13 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 I processori SIMD di tipo processor array (detti anche processori matriciali) forniscono invece ottime prestazioni solo per quei programmi che contengono una alta percentuale di istruzioni vettoriali. Tipiche applicazioni per queste macchine sono quelle per l’elaborazione delle immagini: la singola immagine è organizzata in forma matriciale e può essere sia sistemata in una memoria comune sia partizionato su più memorie locali, a seconda del tipo di elaborazione da compiere. Da notare che una differenza fondamentale tra i processori vettoriali e i processori matriciali è che questi ultimi sono dotati di un dispositivo di comunicazione dei dati che invece i processori vettoriali non hanno. Una variante dei processori matriciali consiste nell’usare un gran numero di processori single-bit. I processori SIMD di tipo associative memory (detti anche semplicemente processori associativi) sono caratterizzati dal fatto che l’insieme delle memorie dati è realizzato tramite una memoria associativa: ogni parola di tale memoria è associata a speciali registri ed alla logica di confronto che compone un processore: Memoria di programma Controllore di Programma Array Controller ALU e registri speciali Memoria associativa Infine, i sistemi di tipo sistolico sono stati pensati per sfruttare la tecnologia VLSI ed il fatto che le interconnessioni semplici sono quelle che consentono implementazioni più economiche, alta densità di integrazione ed alte prestazioni; tali sistemi inglobano contemporaneamente i concetti di pipeline, parallelismo e regolarità nelle interconnessioni. In generale, un sistema sistolico è una rete di processori, collegati tramite interconnessioni dedicate, secondo topologie regolari. Ciascun processore autonomamente calcola dati e li trasmette attraverso le proprie interconnessioni. Il “pulsare” regolare dei processori conduce ad un flusso di dati regolare e continuo attraverso l’intero sistema. Questo tipo di architetture sono molto usate nell’elaborazione dei segnali, dove infatti l’uscita deve essere cadenzata così come è cadenzato l’ingresso dei dati. A Arrcch hiitteettttu urree M MIIM MD D Come più volte detto in precedenza, in questo caso il programmatore può controllare più flussi di istruzioni e più flussi di dati. Le due alternative sono tra architetture a memoria condivisa ed architetture a memoria distribuita e sono state già esaminate in precedenza. Autore: Sandro Petrizzelli 14 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo P Paarraad diiggm mii M MIIM MD D Le macchine appartenenti a questa classe sono tutte basate sui principi MIMD di operazioni asincrone e manipolazione concorrente di più istruzioni e più flussi di dati. Si tratta in genere di macchine ancora sperimentali. I processori ibridi MIMD/SIMD sono architetture che consentono a parti selezionate di una architettura MIMD di essere controllate in maniera SIMD. In queste macchine, è fondamentale avere dei collegamenti a larghissima banda, in modo che dati ed istruzioni possano “spostarsi” rapidamente tra le unità di elaborazione, rendendo minimi i temi morti: in genere, le unità di elaborazione sono poche e molto veloci e sono collegate da fibre ottiche. Un processore ibrido a flussi di dati (o data flow) prende le basi da una rappresentazione grafica particolare, introdotta da Dennis: in pratica, si utilizza un grafo in cui i nodi corrispondono a operatori e gli archi corrispondono a canali di comunicazione per trasmettere valori da un operatore all’altro; la particolarità è che le operazioni non vengono eseguite dai nodi secondo una sequenza definita dal programma: ogni nodo esegue la propria operazione solo nel momento in cui possiede tutti i dati necessari, dopodiché trasmette il risultato al nodo successivo. Questo schema computazionale è molto simile ad una corsa a staffetta: ogni atleta (nodo) parte non appena ha ricevuto il testimone (dato) dall’atleta che ha percorso il tratto precedente di gara. Naturalmente, proprio per il fatto che ogni nodo esegue il compito assegnatogli solo quando possiede tutti i dati necessari, non c’è il pericolo di eseguire operazioni prematuramente, così come, ad esempio, non ci può essere il tentativo di molte unità di elaborazione di leggere simultaneamente una stessa locazione di memoria. Il vantaggio finale delle architetture a flusso di dati è che si evitano i casi di strozzatura delle architetture parallele e, allo stesso tempo, si offre un nuovo modo per sfruttare il parallelismo implicito nei programmi. Per concludere con questa panoramica, nei primi anni ’80 Kung sviluppò i concetti del wave front array: il principio di fondo è che, quando un processore ha eseguito i suoi calcoli ed è pronto a passare i dati al successore, lo informa di ciò ed aspetta che questi gli dia il via per il trasferimento. Confronto tre architetture seriali, pipelined e parallele Consideriamo l’esecuzione di tre processi (A, B e C) su un calcolatore; supponiamo che ogni processo richieda un certo numero di variabili di ingresso (ad esempio due, denominate x ed y) e produca un certo numero di variabili in uscita (ad esempio una sola, denominata z). Supponiamo inoltre di poter dividere ogni singolo processo in 4 sottoprocessi (S1, S2, S3 e S4). Nella prossima figura vengono confrontate le modalità di esecuzione dei vari sottoprocessi in tre architetture: • architettura seriale classica (SISD); • architettura seriale con pipeline (MISD); • architettura parallela con tre processori (MIMD). aggiornamento: 15 luglio 2001 15 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 xA yA S1 S2 S3 zA S4 xB yB S1 S2 S3 zB S4 xC yC S1 S2 S3 S4 zC Esecuzione seriale xA yA S1 xB yB zA S2 S3 S4 S1 S2 S3 S4 S1 S2 S3 xC yC zB zC S4 Esecuzione pipelined xA yA S1 S2 S3 S4 zA xB yB S1 S2 S3 S4 zB xC yC S1 S2 S3 S4 zC Esecuzione parallela E’ ovvio che, nell’esecuzione pipelined, si suppone che non ci siano ostacoli (quelli che nel capitolo 6 abbiamo chiamato conflitti) all’esecuzione contemporanea di più sottoprocessi. Analogamente, nell’esecuzione parallela, si suppone che siano disponibili almeno tre processori, uno per ciascun processo, e che i tre processi siano del tutto indipendenti tra loro, proprio per consentire la contemporaneità della loro esecuzione. Sull’esecuzione seriale dei programmi, abbiamo ben poco da dire. Al contrario, i prossimi due paragrafi forniscono qualche dettaglio in più circa il pipelining e l’esecuzione parallela. P Piip peelliin niin ngg Il concetto di pipelining può essere applicato fondamentalmente a tre “livelli” distinti: • pipeline a livello di processore (processor pipeline); • pipeline a livello di istruzioni (instruction pipeline); • pipeline a livello di operazioni aritmetiche (arithmetic pipeline). Autore: Sandro Petrizzelli 16 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo La seconda e la terza tipologia di pipelining sono quelle presenti nei processori vettoriali (specialmente il pipeline aritmetico). Il pipeline a livello di processori corrisponde invece ad usare una catena di processori funzionanti in pipeline. In ogni caso, il pipelining è un esempio di “sistema MISD” , in cui cioè un insieme multiplo di istruzioni opera contemporaneamente sullo stesso insieme di dati. Le figure seguenti schematizzano le tre possibilità: CPU MEM CPU MEM CPU MEM Processors Pipeline. Si suppone che ciascuna memoria abbia una doppia porta di accesso. Si suppone inoltre che il funzionamento sia sincrono, perché solo in questo caso si parla rigorosamente di pipeline. Potrebbe capitare che un processore sia più veloce di altri: si potrebbe allora pensare di farlo lavorare su quantità di dati maggiori rispetto agli altri, ad esempio operando procedure di sovracampionamento dei dati in ingresso FETCH istr i MEM DECOD istr i-1 FETCH istr i-2 EXE istr i-3 ALU MEM Instructions Pipeline MEM R 1 ALU 1 R 2 ALU 2 R n ALU n MEM Arithmetic Pipeline Soffermiamoci sull’ultima figura, in cui abbiamo schematizzato il pipelining a livello di operazioni in virgola mobile (ossia quindi il pipelining dello stadio EX, con riferimento alla terminologia usata nel capitolo 6): con R 1 , R 1 ,…,R n-1 abbiamo indicato i risultati parziali delle operazioni in virgola mobile, mentre ovviamente R n è il risultato finale. Nel caso di pipeline vettoriale, quando il primo scalare esce da R1, entra lo scalare successivo, in modo che, in condizioni di regime si abbia uno nuovo scalare per ogni ciclo di clock. Segnaliamo infine che il concetto di pipelining, almeno in generale, è applicabile a qualsiasi livello : intero sistema, singolo processo, singola istruzione, singola operazione, singolo stadio di esecuzione di una operazione (livello più basso). A prescindere dal livello cui venga applicato, il pipelining è tale per cui, all’aumentare del numero di stadi della pipeline, aumentino anche le probabilità di conflitti: abbiamo ad esempio visto, nel capitolo 6, che quanto maggiore è il numero di stadi della pipeline di esecuzione delle istruzioni nel DLX, tanto maggiore è la probabilità di avere conflitti di dati. aggiornamento: 15 luglio 2001 17 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 P Paarraalllleelliissm mii In un calcolatore, il parallelismo si può ottenere in vari modi: • parallelismo temporale→pipeline (vector processor): abbiamo una rete di unità di esecuzione, con una sola unità di controllo; • parallelismo spaziale sincrono→ array processor: abbiamo tanti processori (omogenei tra loro) che scambiano dati in modo sincrono, nonostante eseguano istruzioni diverse; i processori sono disposti secondo una matrice, in modo che ciascuno possa comunicare con quelli che ha attorno (che generalmente sono 4 se la matrice è quadrata, ma possono anche essere di più o di meno). Proprio per questa particolare disposizione si parla di parallelismo “spaziale”; • parallelismo spaziale asincrono→multiprocessori: abbiamo una rete di processori autonomi ed eterogenei; essi si scambiano informazioni secondo protocolli del tipo domanda→risposta. Rispetto al caso precedente, la disposizione può anche essere la stessa, ma in generale manca la sincronizzazione tra i processori. Ci sono del resto casi in cui i processori sono comunque sincronizzati, come avviene ad esempio nelle macchine SIMD. Possiamo inoltre fare la seguente classificazione: 10 • elaborazione concorrente (concurrent processing): si ha quando un sistema di calcolo esegue il suo carico elaborativo costituito da più processi indipendenti (ed eventualmente eterogenei tra di loro), eseguibili in tempi successivi ed eventualmente sotto il controllo di un’unica unità di controllo. In questo caso, dunque, i vari task (o compiti o processi) “concorrono” per la produzione del risultato finale nonché per l’uso dell’unica unità di controllo; • elaborazione distribuita: si ha quando un sistema di calcolo esegue il suo carico elaborativo costituito da un insieme di processi (concorrenti nell’esecuzione del codice) eseguibili contemporaneamente su diverse unità di elaborazione (PU, Processing Unit); esiste ovviamente una unità di elaborazione che potremmo definire master e che ha il compito di ripartire il carico di lavoro tra i vari nodi del sistema; • elaborazione parallela: si ha quando il carico elaborativo del sistema di calcolo è costituito da più “parti” (dette unità atomiche ( 10) o moduli) eseguibili nello stesso tempo su differenti unità di calcolo (fondamentalmente delle ALU), che sono in grado di scambiarsi dati d’utente e dati di controllo. Questo tipo di elaborazione presuppone dunque che una applicazione venga decomposta in più moduli, omogenei tra loro, che interagiscano tra loro mediante opportuni meccanismi di comunicazione e sincronizzazione e che possano essere eseguiti in concorrenza come processi paralleli: si parla in questo caso di programmazione concorrente (più processi su più processori) da non confondere con la multiprogrammazione (più processi contemporanei su un solo processore). Cioè non ulteriormente divisibili Autore: Sandro Petrizzelli 18 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo I processori con pipeline (MISD) e gli array processor (SIMD) effettuano l’elaborazione parallela, mentre invece i multiprocessori (MIMD) eseguono l’elaborazione concorrente ( 11) . Per poter effettuare il calcolo parallelo, è necessario che l’insieme dei processi locali che operano concorrentemente possano scambiarsi tra loro dei dati medianti appositi canali di comunicazione logici; questi ultimi non devono necessariamente essere fisici. Lo scopo del calcolo parallelo è quello di migliorare le prestazioni semplicemente ripartendo le attività su un numero crescente di nodi della rete (definizione di scalabilità) . Occorre dunque avere una architettura che consenta lo svolgimento in parallelo del maggior numero di operazioni, allo scopo di ridurre il tempo totale di elaborazione. Il parallelismo può essere di quattro tipi: 1. parallelismo a livello di fasi di esecuzione di singole istruzioni (pipeline); 2. parallelismo a livello di accessi gerarchici alla memoria (shared memory); 3. parallelismo nell’esecuzione di una singola istruzione su strutture dati (processori vettoriali); 4. istruzioni multiple interagenti (ci sono cioè più istruzioni contemporaneamente ed in grado anche di interagire tra loro). eseguite Le prime due tipologie di parallelismo non alterano la struttura dei programmi, mentre invece le altre due richiedono espressamente che vengano isolate le operazioni da eseguire in parallelo. Valutazione delle prestazioni Possiamo subito distinguere due casi: • componenti di calcolo: in questo caso, dobbiamo valutare le prestazioni di picco di uno specifico componente, fornendo le sue caratteristiche di targa ( 12), ossia caratteristiche che non siano degradate dall’uso di periferiche o di memorie o di altro; • sistema di calcolo: in questo caso, dobbiamo valutare la risposta di un software complesso (inteso come composizione di software applicativo e software di configurazione) eseguito su un determinato hardware. Fondamentalmente, l’analisi ed il progetto di un sistema di calcolo dotato di più processori presuppongono che si valuti l’aumento della capacità di calcolo ottenibile aumentando il numero di processori e lasciando invariata la tecnologia adottata. I parametri di prestazione cui prestare attenzione sono i seguenti: 11 In quest’ultima affermazione si tenga conto che ciascun processore di una macchina MIMD può anche eseguire più processi: di conseguenza, l’eventuale elaborazione concorrente avviene quando, sul sistema MIMD nel suo complesso, ci sono più processori che lavorano per arrivare ad un risultato unico, anche se ciò non esclude che gli stessi processori si stiano dedicando anche ad altro. Del resto, se non fosse così, se cioè o gni processore si potesse dedicare solo ad un processo, una macchina MIMD con N processori potrebbe eseguire solo N processi. 12 Le attuali architetture di microprocessori tendono a fornire elevate prestazioni su piccoli programmi “benchmark”, facendo uso di registri, memorie cache, ecc. aggiornamento: 15 luglio 2001 19 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 • tempi di elaborazione, sia assoluti sia relativi (cioè confrontati rispetto al clock oppure rispetto ad altre macchine); • efficienza computazionale (riferita cioè all’impiego delle risorse di calcolo); • accelerazione, ossia guadagno di velocità rispetto al sistema monoprocessore (con uguale tecnologia): per esempio, se la scalabilità è massima, l’uso di N processori dovrebbe idealmente garantire una accelerazione pari proprio ad N; • quantità di memoria locale necessaria (ad esempio, deve essere sufficiente per non dover spezzare in più parti un unico codice sequenziale); • rapporto costo/prestazioni. Gli obbiettivi cui bisogna mirare sono i seguenti: • massimo parallelismo del codice eseguibile; • massimo bilanciamento, tra le varie unità di elaborazione, del carico computazionale; • minima attività di comunicazione tra i vari nodi (unità di elaborazione) del sistema di calcolo; • ottimizzazione della quantità di memoria a disposizione del singolo nodo; • ottimizzazione della potenza di calcolo di ciascun nodo. Strutture di interconnessione Si è detto più volte che, in un sistema dotato di più unità di elaborazione (sistema parallelo), devono essere presenti delle adeguate strutture di interconnessione per lo scambio di informazioni. Si è verificato che proprio tali strutture sono spesso i veri colli di bottiglia per un sistema parallelo ( 13), in quanto introducono delle forti limitazioni sul numero di processori collegabili e possono essere oggetto di contesa da parte di più processori. Un concetto fondamentale, per le strutture di interconnessione, è la cosiddetta banda passante: è la portata (espressa in bit per secondo, bps) della rete di interconnessione tra i vari processori nonché tra i processori ed il mondo esterno. Banda passante di un sistema di calcolo Consideriamo il caso semplice di un sistema monoprocessore con h processi da eseguire: indicando con b la banda passante del processore, la banda passante del sistema (cioè il numero di eventi o processi da eseguire) risulta pari a B h (1) = b ⋅ (1 − rT(h ) ) − K In questa espressione, rT(h) è la frazione di carico computazionale spesa per l’attività di routing, ossia per l’instradamento dei dati nel rispetto della topologia 13 Qualcosa di analogo avviene per il bus nei sistemi a singolo processore del tipo Von Neumann Autore: Sandro Petrizzelli 20 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo adottata nel sistema. La costante K, invece, è una costante correttiva e tiene conto di altri eventi paralleli accessori: ad esempio, tiene conto delle routine eseguite dal sistema operativo. Per estendere quella formula al caso di N processori, ci basta sostituire il termine additivo 1 proprio con N ed eliminare K: B h ( N) = b ⋅ (N − rT(h ) ) Questa quantità raggiunge evidentemente il suo limite superiore (=b×N) quando non c’è carico dovuto al routing, per cui rT(h) diventa pari ad 0. Legge di Amdahl applicata ad una macchina con N processori Quando abbiamo studiato la legge di Amdahl, ne abbiamo dato la seguente rappresentazione analitica: Accelerazione = 1 (1 - Frazione migliorata )+ Frazione migliorata Accelerazione migliorata Essa afferma sostanzialmente che l’accelerazione ottenibile introducendo una qualche miglioria su una macchina è legata strettamente alla frazione di computazione che risente positivamente delle migliorie, frazione che deve essere quanto più grande possibile. Se partiamo da una classica macchina sequenziale monoprocessore (SISD), una possibile miglioria consiste nell’aggiungere un certo numero di processori (passando dunque ad una macchina parallela) in grado di operare in parallelo. Il nostro obbiettivo è dunque quello di verificare se e quale accelerazione è ottenibile con l’introduzione di tali processori. Cominciamo ad elencare i termini che utilizzeremo: • indichiamo con h il grado di parallelismo del carico elaborativo che intendiamo eseguire, ossia il numero di processi che possono essere eseguiti in parallelo; • indichiamo con T h (1) il tempo che sarebbe richiesto da un sistema monoprocessore per completare il carico computazionale dovuto agli h processi e con T h (N) il tempo che sarebbe invece richiesto, per lo stesso carico computazionale, da un generico sistema ad N processori; • infine, indichiamo con f la porzione non parallelizzabile del codice, ossia quella che deve essere necessariamente eseguita in modo sequenziale (tramite quindi un unico processore). Si può vedere facilmente che la quantità Th (N) è calcolabile nel modo seguente: Th ( N ) = f ⋅ Th (1) + (1 − f ) ⋅ aggiornamento: 15 luglio 2001 21 Th (1) N Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 Il senso della formula è abbastanza evidente: il tempo totale richiesto dal sistema multiprocessore è la somma del tempo richiesto dal sistema monoprocessore, pesato per la frazione di codice non parallelizzabile, e di un contributo che sfrutta i vantaggi all’elaborazione parallela; quest’ultimo contributo, pesato per la frazione di codice parallelizzabile, è infatti dato dal tempo richiesto dal monoprocessore diviso per il numero di processori disponibili. Il caso ideale sarebbe ovviamente quello in cui f=0, in quanto avremmo banalmente Th ( N) = Th (1) , ossia una riduzione del tempo di elaborazione di un N fattore pari al numero di processori a disposizione. Al contrario, per f≠0, si ha una penalizzazione dovuta alla frazione di codice che non è parallelizzabile e quindi non può usufruire della presenza dei vari processori, ma ne utilizzerà uno solo. A questo punto, diventa immediato calcolare l’accelerazione ottenuta col sistema ad N processori, confrontando i tempi totali tra il suddetto sistema e quello a singolo processore: Accelerazione = Th (1) = Th ( N) 1 f + (1 − f ) ⋅ 1 N = N N = f ⋅ N + (1 − f ) 1 + f ⋅ ( N − 1) Da notare che abbiamo qui calcolato il rapporto tra T h (1) e T h (N) e non viceversa, per il semplice motivo che T h (1) è il tempo più lungo, per cui la frazione così calcolata risulta maggiore di 1 e quindi effettivamente indicativa di una accelerazione. E’ importante chiarire che l’accelerazione così calcolata è relativa ad N processori e h processi, per cui spesso si trova quella formula scritta nel modo seguente: S h ( N) = Th (1) N = Th ( N) 1 + f ⋅ (N − 1) dove S sta per “speed up”, ossia appunto “accelerazione”. Facciamo un rapido esempio numerico: supponiamo che i processori a disposizione siano 1000 e che anche i processi da eseguire siano 1000; supponendo che la frazione di codice non parallelizzabile sia pari all’ 1% (quindi f=0.01), otteniamo una accelerazione pari a S1000 (1000) = 1000 = 91 1 + 0.01 ⋅ (1000 − 1) Risulta evidente, da questo calcolo, che l’incremento di prestazione ottenuto (quantificato appunto dall’accelerazione) è decisamente non proporzionato al numero di processori: in particolare, una accelerazione pari a 91, rapportata a 1000 processori, dice sostanzialmente che il sistema viene impegnato per meno del 10% delle sue prestazioni di picco e questo a causa di una piccola porzione sequenziale (1%). Solo nel caso ideale in cui fosse f=0, otterremo una accelerazione perfettamente commisurata all’incremento del numero di processori ( 14). 14 Ci sono alcune ragioni per cui non potrà mai essere f=0: ad esempio, l’inizializzazione dei parametri è un tipico processo non parallelizzabile, come anche la raccolta dei risultato. Tanto per fare un esempio, consideriamo l’elaborazione di una immagine: essa dovrà essere acquisita da un solo processori e solo dopo potrà essere elaborata da più processori (ad Autore: Sandro Petrizzelli 22 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo Un modo abbastanza intuitivo per sintetizzare quest’ultimo concetto consiste nel calcolare l’accelerazione per N che tende ad infinito (cioè l’accelerazione asintotica): 1 1 N = lim = N → ∞ 1 + f ⋅ (N − 1) N →∞ 1 1 f + f ⋅ 1 − N N lim Sh ( N) = lim N →∞ Questo risultato dice che, a prescindere da quanti processori utilizziamo (purché ovviamente in numero elevato), l’accelerazione ottenibile è comunque superiormente limitata da 1/f ; al crescere di f, il limite scende in maniera inversamente proporzionale. Ad esempio, se abbiamo un programma costituito per il 10% da calcoli puramente sequenziali, non potremo mai ottenere una accelerazione superiore a 1/0.1=10, anche se usassimo infiniti processori. Questa forte limitazione di prestazioni è dovuta essenzialmente al fatto che, nonostante la presenza di molti processori, alcuni di essi non vengono comunque usati per tutto il tempo di esecuzione, data la presenza del codice non parallelizzabile: l’accelerazione è tanto minore quanto maggiore è il numero di processori effettivamente impegnati. Una misura quantitativa di quest’ultimo concetto si ottiene facendo il rapporto tra l’accelerazione ed il numero N di processori, in modo da ottenere l’efficienza di utilizzo dei processori stessi: η= S h ( N) 1 = N 1 + f ⋅ (N − 1) Bisogna a questo punto precisare una cosa: questo modo di studiare una macchina dotata di più processori potrebbe risultare troppo pessimistico nel caso di alcuni tipi di architetture ; in particolare, esso risulta applicabile solo per macchine di tipo MIMD (in cui ci sia parallelismo asincrono dei dati, cioè più processi operanti in modo indipendente su più dati), mentre per altre architetture (ad esempio le SIMD) bisogna fare altre considerazioni, cui sono destinati i prossimi paragrafi. Legge di Braunl per architetture SIMD In questo paragrafo ci concentriamo specificamente su una macchina SIMD (cioè con flusso singolo di istruzioni e flussi multipli di dati): a tal proposito, ricordiamo che una simile macchina può essere pensata come costituita da una sola unità di controllo che acceda ad una sola memoria istruzioni ed invii, ciclo per ciclo, la stessa istruzione da eseguire a un insieme di unità di esecuzione. Un programma SIMD contiene comandi sia scalari sia vettoriali e la sua esecuzione è simile a quella di un programma eseguito in modo sequenziale sulla macchina di Von Neumann: infatti, un singolo comando vettoriale di un sistema SIMD con N processori può essere implementato monoprocessore tramite N comandi eseguiti in sequenza . su un sistema La terminologia che useremo in questo paragrafo è solo leggermente diversa rispetto al paragrafo precedente: esempio suddividendola in tante righe quanti sono i processori); al termine dell’elaborazione, essa dovrà essere ricostruita nuovamente da un solo processore. aggiornamento: 15 luglio 2001 23 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 • indichiamo con N il numero di componenti di un singolo comando vettoriale SIMD; • indichiamo con T N (1) il tempo necessario per elaborare un comando vettoriale ad N componenti tramite un unico processore ( 15); indichiamo invece con T N (N) il tempo richiesto per eseguire lo stesso comando tramite N processori ( 16); • infine, indichiamo con f la porzione non parallelizzabile del codice, ossia quella che deve essere necessariamente eseguita in modo sequenziale. Possiamo subito calcolare la quantità TN (1): se il codice fosse interamente parallelizzabile (f=0), il tempo di esecuzione richiesto al sistema monoprocessore sarebbe banalmente pari ad N volte il tempo richiesto dal sistema con N processori; al contrario, in presenza di una frazione f non nulla di codice non parallelizzabile, bisogna tener conto della relativa penalizzazione, per cui risulta TN (1) = f ⋅ TN ( N) + (1 − f ) ⋅ N ⋅ TN ( N) Abbiamo qui calcolato quanto tempo è necessario per elaborare un comando vettoriale ad N componenti su un unico processore sapendo quanto tempo impiegano N processori e sapendo che esiste una frazione f≠0 di codice non parallelizzabile. Questa è la legge di Braunl. Usandola per calcolare l’accelerazione del sistema SIMD rispetto ad un classico sistema monoprocessore, otteniamo evidentemente che S N ( N) = TN (1) = f + (1 − f ) ⋅ N TN ( N ) Se facciamo lo stesso esempio numerico fatto nel paragrafo precedente (cioè N=1000 e f=0.01), otteniamo una accelerazione pari a 990, quindi molto prossima ad N (al contrario di quanto ci veniva indicato dalla legge di Amdahl, che forniva una accelerazione pari appena a 91) . In generale, la formula ottenuta, confrontata con la legge di Amdahl applicata a macchine MIMD (e solo a quelle), ci dice che, mentre con le macchine MIMD l’accelerazione ottenuta è tanto maggiore quanto minore è f (cioè diminuisce linearmente con 1/f), con le macchine SIMD essa diminuisce al diminuire di f (in particolare, l’accelerazione diminuisce quasi linearmente con f) . In questi discorsi bisogna parallelizzabile è data da tener conto f= che la frazione di codice non f SIMD N A parità di f SIMD , maggiore è il numero dei processori impegnati in un comando SIMD e minore diventa la frazione f. 15 Come si è detto prima, questo equivale ad eseguire N comandi in sequenza equivalenti all’unico comando vettoriale in questione. 16 Un sistema che mette a disposizione tanti processori quanti sono i processi da eseguire prende il nome di paracomputer. Autore: Sandro Petrizzelli 24 aggiornamento: 15 luglio 2001 Parallelismo nei sistemi di calcolo Tempo di comunicazione interinter - processor Nei due precedenti paragrafi, abbiamo calcolato l’accelerazione di sistemi multiprocessore (MIMD e SIMD) adottando una ipotesi di fondo: abbiamo trascurato il tempo che nel sistema viene “perso” per la comunicazione tra i vari processori costituenti il sistema. Al contrario, per valutare in modo compiuto le prestazioni di una macchina dotata di più processori, dobbiamo necessariamente includere tale tempo, che indichiamo con C (detto appunto tempo di comunicazione interprocessor). Supponiamo allora che il codice da eseguire sia completamente parallelizzabile, per cui supponiamo f=0. In questo caso, seguendo i discorsi dei paragrafi precedenti, il tempo di elaborazione del sistema ad N processori risulterebbe idealmente pari al rapporto tra il tempo di elaborazione del sistema monoprocessore ed N stesso: Th ( N) = Th (1) N In realtà, dobbiamo includere il tempo di comunicazione inter-processor, per cui scriviamo più correttamente che Th ( N ) = Th (1) +C N Calcolando l’accelerazione (mantenendo l’ipotesi di f=0), otteniamo S h ( N) = La quantità C Th (1) Th (1) Th (1) NTh (1) N = = = Th ( N ) Th (1) Th (1) + NC 1 + C N +C Th (1) N è chiaramente il tempo di comunicazione inter-processor espresso in rapporto al tempo di elaborazione del single-processor: la si indica normalmente con c, per cui scriviamo che S h ( N) = N 1+ c ⋅ N Rispetto al valore ideale di accelerazione (=N) calcolato per f=0, abbiamo dunque una penalizzazione di un fattore (1+c⋅N), che sarebbe evidentemente nulla se fosse c=0. Legge di Amdahl modificata I discorsi dei paragrafi precedenti portano alla introduzione della cosiddetta legge di Amdahl modificata. Useremo la seguente terminologia: • ε è la possibilità che un dato programma non possa essere ripartito su N processori (coincide dunque con la frazione non parallelizzabile del codice, che in precedenza abbiamo indicato con f); aggiornamento: 15 luglio 2001 25 Autore: Sandro Petrizzelli Appunti di “Calcolatori Elettronici” – Capitolo 10 • δ è il cosiddetto sbilanciamento relativo del carico tra gli N processori impiegati, di cui parleremo tra un attimo; • infine, δ/N è il tempo eccedente il tempo di elaborazione ottimale (pari ad 1/N), dovuto appunto allo sbilanciamento. Per quanto riguarda lo sbilanciamento del carico, esso è definito come la differenza tra il tempo massimo ed il tempo minimo di elaborazione dei vari processori. Nel caso tale differenza venga normalizzata al tempo massimo, si parla di sbilanciamento relativo (simbolo δ ): sbilanciamento = T max – T min sbilanciamento relativo = δ = Tmax − Tmin Tmax E’ evidente che lo sbilanciamento tiene conto delle perdita percentuale che si subisce quando un processore termina prima di un altro il proprio carico di lavoro, divenendo così inattivo e quindi inefficiente. Il massimo valore dello sbilanciamento (δ=1, causato da T max >>T min ) si ottiene evidentemente se uno qualsiasi dei processori rimane completamente inattivo. Nel caso in cui ε→0 e non tenendo conto del tempo di comunicazione tra i vari processori, la legge di Amdahl modificata dice semplicemente che S h ( N) = N 1+ δ Questa espressione dice che l’accelerazione ottenuta con gli N processori, supponendo il codice penalizzata, rispetto al completamente parallelizzabile, è comunque valore ideale N, dallo sbilanciamento : se lo sbilanciamento raggiungesse il suo massimo valore (δ=1), l’accelerazione risulterebbe addirittura dimezzata (=N/2), mentre invece, in assenza di sbilanciamento (δ=0), si raggiungerebbe il valore reale. Nel caso più realistico in cui il codice presentasse una frazione non parallelizzabile non nulla (ε≠0), allora la legge di Amdahl modificata dice quanto segue: Sh ( N) = N (1 − ε)(1 + δ) + c ⋅ N + ε ⋅ log 2 N In questa espressione (ottenuta mettendo insieme i vari contributi al tempo T h (N) visti nei precedenti paragrafi), il termine c⋅N tiene conto della penalizzazione dovuta alla comunicazione tra i vari processori, mentre invece il termine ε⋅log 2 N è un termine correttivo sul quale non approfondiamo. Autore: Sandro Petrizzelli e-mail: [email protected] sito personale: http://users.iol.it/sandry Autore: Sandro Petrizzelli 26 aggiornamento: 15 luglio 2001