![]() |
|
|||||||
| Registrazione | Donate | FAQ | Lista utenti | Calendario | Gallery | Segna forums come letti |
| Notices |
![]() |
|
|
LinkBack | Strumenti discussione |
|
|
#1 (permalink) |
|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 682
![]() |
CISC = Complex Istructions Set Computing
RISC = Reduced Instruction Set Computing Come potete notare dagli acronimi la differenza sostanziale sta nel set di istruzioni; ogni CPU ha un proprio set di istruzioni (si parla di istruzioni in linguaggio macchina, ossia in assembler); il set di istruzioni è l'insieme delle istruzioni che una CPU può eseguire; le istruzioni di base sono: -- istruzioni di accesso alla memoria (load / store); -- istruzioni "su registri" (add, sub...); -- istruzioni di salto (condizionato e non); queste, come già detto, sono istruzioni "base"; ossia, una CPU con questo set di istruzioni è in grado di fare poco, anzi, pochissimo Ovviamente le CPU odierne (parlando di P4 e Athlon, a 32bit, non che a 64bit la storia cambi di molto) hanno un set molto più esteso che comprende le istruzioni per la multimedialità (MMX e SSE) e non solo queste ovviamente. CISC: è un'architettura costituita da un set di istruzioni complesse; all'epoca in cui "sono nati" processori CISC non esisteva il concetto di una memoria veloce ma piccola (la cache dei processori odierni) da affiancare al processore e dato che ogni nuovo set di istruzioni deve essere retrocompatibile, la maggior parte delle istruzioni prevedeva molti accessi in memoria (RAM) per lettura / scrittura; la memoria di sistema (RAM) è schematizzabile come come un array, cioè un insieme ordinato di celle aventi una certa dimensione in byte; il processore è schematizzabile, per i nostri scopi, come un elemento che, dietro i comandi impartiti da una logica di controllo (le operazione che la CPU deve compiere sono impartite da una CU, Control Unit che lavora su microistruzioni), preleva i dati dalla memoria centrale (fase di read o load), li elabora (fase di execute), e una volta processati scrive i risulati finali (fase di write back o store) nuovamente in memoria. Il problema di una tale soluzione è che il concetto di memoria è molto "nebuloso": un dispositivo di memorizzazione può essere un registro (che funziona alla velocità di clock del processore in cui è inglobato), una cache, una RAM di sistema o ,alla peggio, l'intero Hard Disk! La maggior parte delle istruzioni CISC fanno uso di modalità di indirizzamento complesso sempre nell'ottica di ridurre la dimensione del codice compilato. Un esempio banale per capire l'approccio CISC all'uso della memoria: abbiamo due valori immagazzinati in due celle di memoria, esempio la cella numero 100 e la cella numero 230. Dobbiamo fare la moltiplicazione fra i contenuti delle due celle e scrivere il risulato nella cella numero 300. Il modo di procedere corretto sarebbe allora questo: Codice assembler per l'operazione di moltiplicazione fra due valori immagazzianti in memoria centrale: MOV A, %100; muovi (move) nel registro A il contenuto della cella 100 MOV B,%230; salva in un altro registro, detto B, il contenuto della cella 230 MUL C,A,B; moltiplica A per B e scrivi il risultato nel registro C STR C, %300; scrivi (store) il valore di C nella cella numero 300 Osserviamo che, in totale, abbiamo 4 istruzioni in assembler. Troppe per i progettisti, secondo la filosofia CISC. Perchè non fare una singola istruzione di moltiplicazione che preveda tutte queste operazioni una volta decodificata dal processore? Se venisse aggiunta nell'ISA del processore, basterebbe scrivere: MUL %300,%230,%100 per impartire l'ordine al processore di salvare il contenuto della memoria nei registri, fare la moltiplicazione e scrivere il risualtato nuovamente in memoria. Tutto quello che abbiamo visto sui CISC è "molto bello", davvero, ma avviene a scapito di un paramentro: l'efficienza nella esecuzione del codice. Guardiamo infatti a questa formula: time/program = [ (instructions/program) x (cycles/instruction) x (time/cycle) ] il membro a sinistra dell'uguaglianza è il paramentro fondamentale che esprime l'efficienza di un processore ad eseguire il dato programma. Più il time/program è basso, meno tempo impiega la CPU a portare a termine il compito. La filosofia CISC tende ad abbattere il termine instructions/program, cioè il numero di istruzioni che compongono il programma. Abbiamo già visto come abbiamo effettuato una riduzione del 75%, portando il numero di istruzioni in assembler da 4 ad 1 semplicemente aggiungendo una nuona istruzionea all'ISA iniziale, nel calcolo di un semplice prodotto! Il problema però è dato dal fatto che se il primo termine a secondo membro scende, magari salgono a dismisura il secondo ed il terzo, cioè il numero di cicli necessari per eseguire la singola istruzione e la durata del singolo colpo di clock. Quindi tutta la fatica fatta per semplificare l'assembler andrebbe a farsi benedire! Cerco di chiarirvi le idee con un esempio estremo: posso fare in modo, al limite, di avere pochissime istruzioni macchina che descrivano un intero programma, ma se poi ogni istruzione richiede un giorno per essere decodificata ed eseguita, perchè troppo complessa, ho peggiorato tutto! I problemi dell'approccio CISC sono proprio questi: 1) le istruzioni contengono un sacco di store e load, cioè di accessi in scrittura e lettura in e dalla memoria centrale la quale, essendo lenta, causa un rallentamento complessivo del sistema. 2) le istruzioni sono tante, complesse, e vanno decodificate prima di poter essere eseguite. E qui veniamo al secondo punto nel calcolo delle performance di un microprocessore : il critical path, la direct execution e la ROM di decodifica. Cominciamo da quest' ultime due: La Direct Execution e la ROM di decodifica Per esecuzione diretta, o direct execution, si intende la capacità del processore di elaborare l'istruzione in linguaggio macchina senza doverla decomporre in elementi più semplici. Ultima modifica di Filippo1974 : 28-11-2007 alle ore 21.14.08. |
|
|
|
|
|
#2 (permalink) |
|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 682
![]() |
...continua...
Per esempio, l'istruzione MUL %300,%230,%100 imponenva una serie di step da eseguire, fra cui tre accessi in memoria, due in lettura (caricamento dei valori delle celle numero 230 e 100) e uno in scrittura (archiviazione del valore del prodotto nella cella 300). Insomma, un bel pò di roba! E tutto quanto deve essere sequenzializzato (in gergo si parla di operazione di scheduling) nella giusta maniera. L'esecuzione diretta prevede che le istruzioni vengano eseguite direttamente, senza una fase di interpretazione delle medesime. Chiaramente, l'hardware deve essere assai complesso per permettere al processore di lavorare in questa maniera. Siccome l'ISA consiste di centinaia di istruzioni, più o meno diverse, è necessario un approccio diverso al problema, in quanto eseguire direttamente istruzioni diversissime fra di loro richiederebbe una quantità improponibile di transistor sul die di silicio. La soluzione sta in un trucco: il processore contiene all'interno un memoria a sola lettura (una ROM, read only memory), che contiene la "traduzione" delle istruzioni complesse in una sequenza di passi più semplici. In altri termini, anzichè essere noi a fornire al processore istruzioni molto semplici ma numerose in forma di assembler, è il processore medesimo che si preoccupa di "espandere" le istruzioni complesse che gli forniamo noi in istruzioni "atomiche" direttamente eseguibili dall'hardware. Questa operazione di decodifica, che si appoggia su una ROM cablata all'interno del processore medesimo, è un punto fondamentale dei processori CISC. Si può pensare di avere una sorta di CPU nella CPU. Dove sta lo svantaggio? Beh, è evidente: se le istruzioni sono troppo complesse la CPU spreca un sacco di cicli preziosi per decodificarle. Non solo: la ROM è interna al processore, e dunque in un certo senso "ruba via" area di silicio altrimenti destinabile all'incremento del numero dei registri o delle unità di esecuzione (unità per eseguire l'addizione o la moltiplicazione fra interi, oppure operazioni in floating point). Se l'accesso in lettura richiede molti cicli per essere portato a termine, le unità di esecuzione stanno lì a girarsi i pollici, perchè la macchina non è abbastanza ottimizzata da poter eseguire altre operazioni mentre una istruzione viene decodificata. Non so se ve ne stiate rendendo conto, ma stiamo elencando tutti i motivi che hanno portato alla nascita del concetto di macchina RISC Chiarita la differenza fra esecuzione diretta ed esecuzione previa decodificazione tramite ROM, passiamo al concetto di critical path. Il critical path Chi è che stabilisce a quanto ammonta il clock di un processore? In altri termini, come faccio a dire "questo processore può raggiungere i 700 MHz di frequenza, mentre quest'altro riesce ad arrivare ad 1GHz"? Bella domanda! Se cercate in giro per siti, leggete fino alla nausea che lo scaling dei transistor verso dimensioni inferiori permette di raggiungere più alte frequenze nominali di funzionamento. È tutto vero, ovviamente, ma la domanda resta senza risposta. Come fannoAMD o Intel (come preferite ) a stabilire sulla carta che un dato processore può toccare certe frequenze? La risposta è: il critical path, ossia il cammino critico. Per cammino critico si intende il percorso più lungo che un segnale deve attraversare nel tempo di un ciclo di clock. Se in qualche punto esiste un percorso che un segnale impiega la bellezza di un microsecondo per percorrere, il clock massimo per quel processore sarà di 1MHz (l'inverso di 1 microsecondo). Non importa se la stragrande maggiornaza degli altri segnali interni impiega per esempio 1 nanosecondo (cioè un millesimo di microsecondo), basta un segnale lento per obbligare tutto il circuito ad andare alla velocità sua, che è quella di 1Mhz al massimo! (Infatti se sulla vosta scehda madre avete due banchi di RAM, uno che lavora a 333MHz e l'altro a 400MHz...entrambi lavoreranno a 333MHz). Inoltre, per problemi legati all'invecchiamento dei componenti, alla deriva termica e al fatto che i transistor non sono mai così precisi come sulla carta, occorre tenersi del margine di progettazione per cui ad 1MHz il dispositivo non andrà mai (a meno che qualche esemplare particolarmente ben riuscito adatto all'overclock non capiti nelle nostre mani.. . Cosa c'entra questo con il CISC? C'entra, perchè un sistema per ovviare al problema del critical path è la pipeline, che nascerà con l'approccio RISC al design di un processore. Il fatto che il un processore CISC, di principio, possa eseguire una sola istruzione alla volta, fa sì che la parte di circuiteria coinvolta nello svolgimento dell'esecuzione più complessa sia quella più lenta, e quindi finisca per fare da tappo alla performance del processore. Tanto per fare un esempio, una moltiplicazione eseguita direttamente, e non tramite successivi steps di addizione, resta tuttavia molto più lenta di una semplice addizione. Anche se la moltiplicazione capitasse una volta ogni 'morte di papa' nel listato assembler, il processore dovrà comunque tenerne conto e pertanto dovrà essere sufficientmente lento da permettere alla moltiplicazione di avvenire senza problemi. Alla fine degli anni Settanta e nei primi anni Ottanta la situazione era cambiata: i compilatori erano divenuti molto più efficienti, le memorie meno costose e i progettisti di microcomputer stavano scoprendo che la "panacea" data dall'implementazione in hardware di istruzioni complicatissime e a volte strampalate stava costituendo un tappo per il miglioramento delle performance. Si cominciò allora a pensare ad un modo diverso di progettare un microprocessore, e le linee guida di progetto possono essere così riassunte: |
|
|
|
|
|
#3 (permalink) |
|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 682
![]() |
...continua...
1) Sulla base di una analisi statistica dei programmi, si scopre che per il 90% del tempo il processore utilizza sempre un ristretto sottinsieme di istruzioni. 2) Perchè allora non ottimizzare il processore nell'esecuzione diretta di queste poche istruzioni lasciando al compilatore l'onere di spezzettare le istruzioni più rare e molto più complesse in task più semplici? In tal modo torna in auge il ruolo del compilatore e si può fare a meno della ROM di decodifica. 3) Non solo: se il processore è in grado di eseguire direttamente in modo ottimizzato poche ma importanti istruzioni, facciamo in modo che ogni istruzione venga completata in un solo ciclo di clock!! (macchina a singolo ciclo) 4) Inoltre, l'esecuzione dei programmi è spesso rallentata dai ripetuti accessi in memoria centrale ordinati dalle varie istruzioni con indirizzamento complesso: decidiamo allora di fare tabula rasa di queste istruzioni e stabiliamo che l'accesso in memoria avvenga esclusivamente tramite due comandi: il load per il caricamento del dato dalla memoria al registro e lo store per la scrittura dal registro alla memoria. 5) Visto che gli accessi in memoria centrale adevono essere limitati il più possibile, occorre disporre sul chip di un consistente numero di registri per avere un magazzino di informazione sufficientemente capiente da consentire l'elaborazione dei dati . Questo insieme di registri è visibile al programmatore in assembler che in tal modo produce un codice altamente ottimizzato per la macchina che deve eseguire il programma. Per capire meglio i punti 4) e 5) facciamo un esempio. Prendiamo questo pezzo di codice: j=0; for (i=0; i<100 ; i++) j = j + i; è scritto in C (o anche J A V A) e dice grosso modo questo: ad ogni ciclo, aggiungi il valore corrente di i a j e salva il risultato in j; fatto questo, incrementa i di una unità e se raggiunge il valore 100 esci dal ciclo. Se il compilatore non è "scemo", è evidente che salva i e j in due registri locali e quindi effettua su di essi le operazioni; solo a ciclo concluso salverebbe i risultati in memoria. Un processore CISC, però, il cui codice assembler è molto poco ottimizzato, potrebbe benissimo prevedere invece una infinità di accessi e di scritture in memoria centrale per prelevare e aggiornare i valori di i e j! (questo fa già intuire come il compilatore in ultima analisi decide la bontà dei benchmark su una macchina anzichè su un'altra. Tutto questo causerebbe un rallentamento terribile delle prestazioni del sistema. Sorpresi? Beh, prima di fare un riassunto su quanto esposto torniamo al "formulone" sul calcolo della performance e facciamo qualche commento: time/program = (instructions/program) x (cycles/instruction) x (time/cycle) (instructions/program): un processore CISC tenta, come abbiamo visto, di diminuire questa quantità. Un RISC, invece, accetta un peggioramento di questo fattore. (cycles/instruction): il processore RISC tende a portare questa quantità al valore unitario ossia un istruzione eseguita per ogni ciclo di clock! (time/cycle): il RISC tende a far diminuire anche questa quantità, che è in sostanza legata al cammino critico discusso prima. Più breve è il critical path, maggiore è la frequenza di clock sopportabile dal processore. Nasce il concetto di pipeline: sfruttando la semplicità delle istruzioni RISC è possibile fare in modo che i vari passi di cui esse sono composte vengano eseguiti in cascata su più istruzioni sequenziali come in una catena di montaggio. Il risultato è la possibilità di eseguire le istruzioni in un solo ciclo di clock che è stata a lungo caratteristica unica dei processori RISC. Inoltre i passi della pipeline possono essere semplici e dotato di critical path basso con conseguenti elevate velocità di clock. Dopo quanto detto è abbastanza evidente come nello scontro CISC vs RISC sia stata quest'ultima filosofia ad aver avuto la meglio. Di processori RISC oggigiorno ne abbiamo piene le letteralmente le tasche: dai palmari ai cellulari della prossima generazione, oggi ogni processore che voglia essere snello e al contempo potente nasce sotto l'effige del RISC. Gli stessi processori x86, come abbiamo visto hanno assimilato il paradigma RISC unica e vincente mossa che ne ha prorogato la vita oltre ogni rosea aspettativa. Del resto il percorso di sviluppo delle capacità di un processore non si ferma a quello che abbiamo detto. L'obiettivo di qualsiasi progettista di processori è quello di ottenere sempre il max throughput complessivo, cioè, il massimo volume di dati elaborati e consegnati in uscita nell'unità di tempo. Come conseguire il massimo rendimento? La parola d'ordine in questi casi è parallelismo. Il parallelismo è stato il passo successivo compiuto dalla tecnologia dei processori dopo l'affermazione del RISC. Questo si esplica principalmente in due modalità differenti all'interno di un singolo processore: il pipelining e il superscaling. Un'altra tecnica di ottimizzazione delle risorse è il multithreading, che come scuola di pensiero ha pochi anni alle spalle ma già promette interessanti innovazioni. La generazione attuale dei microprocessori ha prestazioni, rispetto ai predecessori, superiori di svariati ordini di grandezza. Anche se il progetto attuale di uan CPU varia enormemente da caso a caso, possono essere individuate alcune linee guida comuni ad ogni progetto; infatti ogni CPU effettua una generazione degli indirizzi, continene unità logico-aritmetiche (ALU), possiede file di registri ed ha un'interfaccia di sistema. Molti (direi tutti ormai) hanno una o più cache on-chip, un TLB (Translation Lookaside Buffer) e tutte le architetture attuali possideono un'unità floating point on-chip. Il progettista, anzi, è meglio dire i progettisti di una CPU, devono affrontare una miriade di problemi. Qui di seguito elencherò i problemi delle CPU moderne: 1) Le latenze della memoria e della cache di secondo livello I primi microprocessori effettuavano il fetch (prelevamento) dell'istruzione direttamente dalla memoria; in questo modo dopo aver inviato una richiesta dati, la CPU doveva attendere un tempo "molto lungo" prima che i dati arrivassero, impedendo così alla CPU di operare alla velocità per cui era stata progettata. L'implementazione della cache secondaria off-chip ha aiutato a migliorare questo inconveniente. Una memoria cache è solitamente di dimensioni limitate (dai 256kb su processori di fascia bassa al 1Mb su processori di fascia alta) e contiene un blocco di indirizzi di memoria comprendenti una piccola sezione della memoria principale. La memoria cache fornisce un accesso più rapido e può inviare i dati alla CPU ad una frequenza maggiore della memoria principale. I sistemi di memoria con cache on-chip possono ulteriormente migliorare il problema, poiché permettono il completamento di un accesso in un singolo ciclo di clock. L'aumento di prestazioni fornito dalla cache di primo livello ha indotto i progettisti ad aumentare sempre di più le dimensioni causando in molte architetture un notevole aumento dello spazio dedicato al progetto della cache; infatti in molte implementazioni la cache occupa più dell'80% della superficie del die. Le performance raggiungono il valore massimo quando l’applicazione può essere eseguita totalmente dentro la cache. Tuttavia, quando l’applicazione, come spesso avviene, è troppo grande per stare nella cache, le performance diminuiscono in modo significativo. La cache di primo livello contiene un range di indirizzi che comprende un sottoinsieme di quelli presenti nella cache secondaria, che a loro volta sono un sottoinsieme degli indirizzi presenti nella memoria principale. |
|
|
|
|
|
#4 (permalink) |
|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 682
![]() |
...continua...
Se la cache on-chip ha il vantaggio di aumentare le prestazioni, la tecnologia attuale e il numero di transistori disponibili ne limitano la dimensione massima. Attualmente 64 Kbyte (32 per i dati e 32 per le istruzioni) rappresentano il limite della cache di primo livello, e la sua implementazione richiede molti milioni di transistor. Questi fattori limitanti della cache on-chip aumentano l‘importanza della cache di secondo livello. Il tempo di accesso di molti dei dispositivi RAM attualmente disponibili è piuttosto elevato rispetto al tempo di clock del processore, e ciò forza i progettisti a trovare soluzioni alternative. L’interleaving della cache è uno degli espedienti utilizzati, dato che permette la sovrapposizione delle richieste in memoria da parte del processore. Questa tecnica si può applicare sia alla memoria principale che alla cache; il più comune è l’interleaving a due o a quattro vie. Questo perché aumentando l’interleaving si riesce a nascondere gran parte del tempo di accesso, ma aumenta considerevolmente anche la complessità richiesta per supportarlo. 2) Le dipendenze dei dati In un programma, le istruzioni vengono caricate dalla cache istruzioni, decodificate ed eseguite. Il dato corrispondente è spesso caricato da un registro, manipolato in un’ALU, e il risultato viene rimesso nello stesso registro o in un altro.Se l’istruzione successiva della sequenza richiede il risultato dell’istruzione precedente per poter essere eseguita, avviene una dipendenza di dati. Per quelle istruzioni che richiedono molti cicli per essere completate, ci può essere un impatto notevole sulle prestazioni nel caso vi siano delle dipendenze. Alcune di queste possono essere eliminate semplicemente riarrangiando il programma in modo che il risultato di una data istruzione non venga utilizzato dalle prime istruzioni seguenti. Un modo per alleviare il problema delle dipendenze dei dati è utilizzare l’esecuzione fuori ordine con ridenominazione dei registri. 3) Diramazioni (salti condizionati) Tutti i programmi di computer contengono diramazioni (branch). Alcune sono non condizionali, cioè il flusso del programma viene interrotto non appena l’istruzione di branch viene eseguita; altre sono condizionali, cioè il branch viene eseguito solo se certe condizioni vengono soddisfatte. Ad esempio una tipica istruzione branch di un processore MIPS (usato soprattutto in ambito dell'automazione industriale ma credo anche su alcune versione del Nintendo) può essere: beq $s1, $s2, L1 beq = branch if equal; questa istruzione assembler confronta il contenuto dei registri $s1 e $s2 (la ALU effettua un'operazione aritmetica, in questo caso una sottrazione) e se sono uguali salta all'etichetta L1, se il contenuto dei due registri non è uguale, continua con l'esecuzione del programma. Le interruzioni del flusso del programma sono presenti in tutti i software, e l’hardware può solo cercare di adeguarsi ai branch nel modo più efficiente possibile. Quando viene presa una diramazione, il nuovo indirizzo al quale il programma deve riiniziare può essere nella cache secondaria oppure no; a seconda di dove è situato il nuovo blocco di istruzioni la latenza aumenta o diminuisce. Poiché il tempo di accesso della memoria principale e della cache secondaria sono molto maggiori del tempo di accesso della cache on chip, il branching spesso degrada le prestazioni del processore. Questo problema è ancora più importante nelle macchine superscalari, dove ad ogni ciclo vengono eseguite più istruzioni. In un certo momento, infatti, a seconda della dimensione della pipeline, numerose istruzioni possono essere in vari stadi di esecuzione; quando viene presa una diramazione, non si conoscono il numero di cicli che saranno necessari per la sua esecuzione. L’implementazione del branching è un importante problema architetturale. Per migliorare le prestazioni molte architettura attuali incorporano una circuiteria per la predizione delle diramazioni, la quale può essere implementata in vari modi. RIDUZIONE DELLA LATENZA DELLE MEMORIE La riduzione della latenza della memoria è un passo critico nell’aumentare le prestazioni di un processore. 1)Interfaccia a larga banda con la cache secondaria Un’interfaccia ideale cache secondaria-processore dovrebbe essere sempre in grado di ricevere una richiesta di dati dal processore e di soddisfare questa richiesta nel ciclo di clock successivo; ci si riferisce a questo comportamento come uno "zero wait state". Per progettare una cache secondaria che sia in grado di raggiungere questo tipo di prestazioni, l’interfaccia deve essere studiata in modo da riuscire a trasmettere i dati sempre alla massima velocità possibile. Il bus dati e il bus indirizzi per molti processori costituiscono l’interfaccia con l’intero sistema: il processore può accedere a qualsiasi tipo di dispositivo in ogni momento. Quando avviene un cache miss, viene spedito sul bus un indirizzo, e si accede alla cache secondaria, trasferendo i dati richiesti alla cache on-chip. Se avviene un cache miss in un bus di sistema condiviso, e il processore sta utilizzando il bus esterno per leggere o scrivere su qualche altro dispositivo, l’accesso alla cache secondaria deve attendere finchè non si sono liberati i bus dati e indirizzi; ciò può richiedere molti cicli di clock, a seconda della periferica a cui si accede. In un sistema con bus dedicato i bus dei dati, degli indirizzi e di controllo per la cache secondaria sono separati dai bus che si interfacciano con il resto del sistema. In questo modo gli accessi alla cache secondaria in caso di miss vengono garantiti sempre, qualsiasi cosa stia facendo il sistema. 2) Accesso a blocchi Quando avviene un cache miss on-chip, esiste un numero di byte, solitamente programmabile, che viene trasferito sul bus ogni volta che si accede alla cache secondaria. Questo numero è la dimensione di una linea di cache; per le architetture attuali la dimensione classica è di 32 byte. Il numero di accessi richiesto per effettuare il riempimento di una linea dipende dalla dimensione del bus di dati esterno del processore. Per esempio, un processore con bus dati a 64 bit che si interfaccia con una memoria a 64 bit impiegherà quattro accessi alla cache secondaria per riempire una linea di cache da 32 byte. Per completare tutto ciò il processore deve generare quattro indirizzi separati e guidare ognuno sul bus indirizzi esterno tramite appropriati segnali di controllo. Utilizzando l’accesso a blocchi il processore deve invece generare solo l’indirizzo iniziale della sequenza, mentre gli altri tre indirizzi vengono generati dalla logica di controllo della cache. 3) Interleaving L’interleaving è una tecnica di progetto utilizzata per aumentare la larghezza di banda della memoria; tale tecnica può essere applicata sia alla cache secondaria che alla memoria principale. Il sistema di memoria più semplice è quello con un solo banco di memoria. Quando si accede a tale banco, deve trascorrere un certo intervallo temporale prima di un secondo accesso; quaesto intervallo dipende sia dal progetto del sistema sia dalla velocità dei dispositivi di memoria utilizzati. Se sono presenti più banchi, allora l’accesso ai banchi può essere sovrapposto. L’abilità di sovrapporre tali accessi aiuta a nascondere le latenze della memoria e diventa sempre più importante al crescere della dimensione dei dati richiesti. Una tipica memoria con interleaving consiste in banchi pari e dispari. Per esempio, il processore richiede dei dati ad un indirizzo pari, così il controller della memoria inizia un ciclo al banco pari. Una volta che l’indirizzo è stato raggiunto dalla logica di controllo della memoria, il processore può generare un nuovo indirizzo, il più delle volte nel ciclo di clock successivo. Se il nuovo indirizzo si riferisce ad un banco dispari, l’accesso alla memoria può iniziare immediatamente; in questo modo, non appena il banco pari ha completato l'operazione, il banco dispari è già pronto a fornire il dato. Pertanto più a lungo si riesce a minimizzare gli accessi sequenziali, più ci si avvicina alle prestazioni di tipo "zero wait state". I più comuni sistemi di memoria con interleaving sono quelli a due e a quattro vie; il numero di banchi e la larghezza di banda di ciascuno sono spesso determinati dal processore. 4) Cache non bloccante In una tipica implementazione il processore agisce sulla cache finchè avviene un cache miss. A questo punto, trascorre un certo numero di cicli prima che i dati vengano riportati nella cache on-chip, permettendo la ripresa dell’esecuzione. Questo tipo di implementazione è detto bloccante, dato che non si può accedere alla cache finchè non viene risolto il cache miss. La cache di tipo non bloccante invece, permette accessi consecutivi anche in caso di cache miss. In questo caso, per aumentare le prestazioni globali del sistema, è cruciale localizzare il prima possibile i miss e effettuare i passi necessari per risolverli. 5) Prefetch Il prefetching è una tecnica con cui il processore può richiedere un blocco di cache prima del momento in cui è effettivamente necessario. L’istruzione di prefetch deve essere integrata nel set di istruzioni, e deve esserci un appropriato hardware per eseguirla. Per esempio, supponiamo che il compilatore stia avanzando in modo sequenziale attraverso un segmento di codice. Il compilatore può fare l’ipotesi che questa sequenza continuerà oltre il range degli indirizzi disponibili nella cache di primo livello, e può richiedere un’istruzione di prefetch, la quale carica il blocco di istruzioni successivo nella cache di secondo livello. Quindi, quando il processore richiede la sequenza successiva, questa può essere eseguita ad una frequenza maggiore; se per qualche motivo tale blocco non è necessario, l’area nella cache secondaria viene semplicemente sovrascritta da altre istruzioni. |
|
|
|
|
|
#5 (permalink) |
|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 682
![]() |
...continua...
Il prefetching permette quindi al compilatore di anticipare la necessità di un dato blocco, e di piazzarlo il più possibile vicino alla CPU. DIPENDENZE FRA DATI 1) Ridenominazione dei registri Per ridurre l’impatto negativo sulle prestazioni delle dipendenze fra i dati vengono utilizzate due tecniche. La ridenominazione dei registri distingue fra registri logici e registri fisici; i registri logici sono mappati dinamicamente nei registri fisici attraverso apposite tabelle che vengono aggiornate ogni volta che un’istruzione viene decodificata. Ogni nuovo risultato viene scritto in un registro fisico; tuttavia, il contenuto precedente di ogni registro logico viene salvato, e può essere recuperato nel caso l’istruzione debba essere abortita a causa di un’eccezione o di una previsione di salto non corretta. Mentre il processore esegue le istruzioni, vengono generati moltissimi risultati temporanei, i quali sono immagazzinati in appositi registri. I valori temporanei diventano permanenti quando la corrispondente istruzione viene "graduata", cioè quando tutte le istruzioni precedenti sono state completate con successo nell’ordine del programma. Il programmatore è consapevole dell’esistenza dei soli registri logici, mentre l’implementazione dei registri fisici è nascosta. La ridenominazione dei registri semplifica il controllo delle dipendenze fra i dati. In una macchina che può eseguire istruzioni fuori ordine, i numeri dei registri logici possono diventare ambigui, poiché ad uno stesso registro può essere assegnata una successione di valori diversi. Ma dato che i numeri dei registri fisici identificano in modo unico ogni risultato, il controllo delle dipendenze non risulta più ambiguo. 2) Esecuzione fuori ordine In un tipico processore che esegue le istruzioni in ordine, ogni istruzione dipende dall’istruzione precedente che produce i suoi operandi, e l’esecuzione non può iniziare finchè questi operandi non diventano validi. Se gli operandi richiesti per eseguire una data istruzione non sono validi, la pipeline stalla finchè tali operandi non diventano disponibili. Poiché le istruzioni vengono eseguite rispettando l’ordine del programma, solitamente gli stalli ritardano tutte le istruzioni seguenti. In una macchina superscalare "in-order", dove vengono eseguite più istruzioni per ciclo, varie istruzioni consecutive possono iniziare simultaneamente l’esecuzione solo se tutti i loro corrispondenti operandi sono validi, altrimenti il processore và in stallo. In una macchina superscalare "out-of-order", ogni istruzione può iniziare la sua esecuzione non appena gli operandi necessari diventano disponibili, senza riguardo per la sequenza originaria. L’hardware effettivamente riarrangia l’ordine delle istruzioni per tenere sempre occupate le varie unità di esecuzione. Questo processo viene chiamato dynamic issuing. PREDIZIONE DEI SALTI 1) Brench Prediction Unit (BPU) Le diramazioni interrompono il flusso della pipeline; pertanto, per minimizzare il numero di interruzioni, sono necessari degli schemi di branch prediction. Le diramazioni accadono frequentemente, in media ogni sei istruzioni; in un’architettura superscalare, dove vengono eseguite anche quattro istruzioni per ciclo, la predizione di tali diramazioni diventa importante. Molti schemi di predizione utilizzano degli algoritmi che tengono traccia del comportamento delle diramazioni l’ultima volta che sono state eseguite. Per esempio, se il circuito che memorizza tali comportamenti mostra che la volta precedente un’istruzione ha preso la diramazione, allora si fa l’ipotesi che questa venga presa ancora. Un’implementazione hardware di questa assunzione significa che il programma invierà allo stesso indirizzo tutte le successive diramazioni. La pipeline ora contiene un’istruzione di salto condizionale e altre istruzioni successive ma in quel momento non si sa se tali istruzioni verranno eseguite; infatti se la diramazione non è stata predetta correttamente, le istruzioni nella pipeline devono essere abortite. Molte architetture implementano un branch stack nel quale vengono salvati gli indirizzi alternativi. Se si prevede che la diramazione non sarà presa, viene salvato l’indirizzo dell’istruzione di branch; in caso contrario viene salvato l’indirizzo immediatamente seguente a tale istruzione. EVOLUZIONE DELLE CPU Durante gli ultimi anni, spinte dalle evoluzioni del mercato sia nella crescente domanda di migliori prestazioni, soprattutto in campo grafico e multimediale, che dall’evoluzione di Internet, le principali aziende produttrici si sono date battaglia, più che nel realizzare sistemi veramente innovativi, nel portare all’esasperazione la ricerca della velocità di esecuzione maggiore per mezzo delle tecniche usate negli ultimi 10 anni raggiungendo e superando, anche grazie a nuovi processi tecnologici (nanotecnologie), la soglia dei 4 GHz di clock. In effetti basta pensare ad Intel che con un ritmo impressionante sorna continuamente CPU con frequenze sempre maggiori...ma credo che ora le acque si siano calmate. A parte l’esecuzione più veloce delle singole operazioni, che attiene prettamente alla tecnologia impiegata nella realizzazione degli elementi logici e alla loro organizzazione, si possono eseguire un numero maggiore di operazioni in parallelo per incrementare le prestazioni ossia si persegue lo sviluppo a livello di parallelismo delle istruzioni (ILP). Le strade maestre per questo obbiettivo sono essenzialmente la realizzazione di pipeline più profonde (cioè con un numero di stadi maggiore) e la superscalarità che devono essere però accompagnate da tecniche di controllo e predizione delle diramazioni e riordino delle istruzioni. L’esecuzione in pipeline è naturalmente favorita da istruzioni di ridotta complessità e questo è uno dei motivi per cui gran parte delle risorse dei moderni processori x86 sono spese in fase di decodifica per scomporre le complesse (e di lunghezza variabile) istruzioni del ISA x86 in istruzioni like-RISC da inviare alle diverse unità di esecuzione. Analogamente tecniche come la predizione delle diramazioni o l’esecuzione fuori ordine contribuiscono a spostare la complessità dal software all’hardware, aspetto che sarebbe in contraddizione con la filosofia RISC, ma vengono adottate da moltissimi processori di ispirazione RISC perché consentono alte prestazioni. Molti processori, implementando l’esecuzione fuori ordine, richiedono anche tecniche di gestione delle false dipendenze dei dati come la ridenominazione dei registri (register renaming, cfr più in alto) che consiste in una mappatura dinamica dei registri architetturali su un set più esteso di registri fisici. Questo avviene anche per un processore RISC come il G4 che possiede un numero maggiore di registri indirizzabili rispetto a un processore di architettura x86 e dovrebbe avvertire meno questa esigenza. I processori più recenti hanno adottato soluzioni tutto sommato convenzionali in questi campi e le poche novità derivano dalla spinta del progresso tecnologico che ha consentito soluzioni quantitativamente diverse soprattutto per gestire l’aumentata profondità delle pipeline (ad esempio si pensi alla pipeline del Pentium4 di ben 20 stadi). |
|
|
|
|
|
#6 (permalink) |
|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 682
![]() |
...continua...
La latenza di memoria continua ad essere una delle grandi sfide dei moderni processori e anche in questo campo le novità riguardano essenzialmente le possibilità offerte dalla integrazione. Prestando particolare attenzione come detto alle applicazioni multimediali che fanno grande uso della computazione intensiva per floating point si è andato affermando negli ultimi anni la tendenza ad estendere il parallelismo ai dati di questa natura per processare un numero maggiore di elementi in minore tempo. Questa tecnica, adottata dalle isctruzioni MMX, 3DNow!, SSE e SSE2 è conosciuta come Single Instruction Multiple Data (SIMD) e si esplicita per mezzo della estensione del set di istruzioni dei processori con apposite istruzioni. Per quanto riguarda questo ultimo aspetto abbiamo implicazioni anche per quanto riguarda l’evoluzione tecnologica dato che ha consentito l’integrazione di apposite unità esecutive dedicate a queste operazioni e ricadute sulla gestione della latenza di memoria dato che questi set di istruzioni prevedono generalmente istruzioni di gestione delle cache. Nel corso di questi anni i maggiori produttori hanno messo a punto processi tecnologici a 0.13 e ora a 0.09 micron a 6 livelli di metallizzazione con connessioni in rame o alluminio che avranno come effetti principali: # Riduzione delle dimensioni dei chip a parità di densità di transistori e quindi dei costi di produzione oppure incremento del numero dei transistori a parità di dimensioni del chip e quindi più unità funzionali oppure una combinazione delle due opzioni; # Riduzione delle tensioni di alimentazione dei core e quindi della potenza assorbita; # Incremento delle frequenze di lavoro dei processori (ciò che dicevo prima a proposito di Intel). Mentre gli ultimi due effetti hanno immediate e ovvie ricadute sulle prestazioni complessive di un processore, la possibilità di integrare un numero maggiore di transistor può essere variamente sfruttata. Innanzitutto la possibilità di integrare più unità funzionali consente una maggiore superscalarità e la specializzazione di unità per specifiche applicazioni. Inoltre esiste la possibilità di dotare di un numero maggiore di buffer le unità dedicate al riordino o al temporaneo stoccaggio delle istruzioni per l’esecuzione fuori ordine o di incrementare le dimensioni delle Branch History Table per consentire accurate predizioni delle diramazioni che supportino adeguatamente le pipeline sempre più profonde. L’aspetto di sicuro maggiore impatto sulla latenza di memoria è però la possibilità sia di incrementare le dimensioni della cache di primo livello (si è giunti ad avere 128 KB di cache L1) che di integrare la cache di secondo livello direttamente su chip. Questa soluzione comporta una maggiore banda a disposizione e una riduzione dei costi complessivi ed è la caratteristica saliente delle versioni dei processori di questi ultimi anni. Di questi aspetti si avvantaggiano soprattutto le architetture x86 dato che necessitano mediamente di un 40% in più di cache istruzioni o magari di precoded cache per la decodificazione delle istruzioni x86 e la sempre maggiore quantità di cache a disposizione ha contribuito notevolmente a colmare il gap di cui godevano nella computazione su interi rispetto i processori RISC. Di fatto le cache L2 on chip occupano già adesso gran parte del die, anche fino al 90% in alcuni processori. Su alcune CPU di fascia alta (P4 Xeon) ci sono già cache di terzo livello (L3-cache). PARALLELISMO DEI DATI Gli algoritmi utilizzati nelle applicazioni multimedia e DSP (Digital Signal Processing) prevedono sostanzialmente l’applicazione della medesima operazione ad un grande numero di dati del medesimo tipo. Un tipico esempio può essere l’inversione di una immagine RGB per determinarne il negativo oppure la riproduzione del movimento in una scena 3D. Il modo migliore di trattare dati di tale natura consiste nel raggrupparne un certo numero all’interno di un vettore e operare su di esso piuttosto che operare sui singoli dati (un dato alla volta) per analogia definiti scalari. La tecnica che sottende a questo procedimento è conosciuta come SIMD ed è diventata una delle parole chiave dei processori di recente realizzazione che la applicano a dati floating point che sono i più coinvolti nelle applicazioni multimediali. A questa scelta, ancora una volta, sono giunti sia processori nativi CISC che RISC sebbene l’introduzione di nuove istruzioni e di nuove unità a livello hardware non appartenga certo al canone RISC. Le scelte che differenziano l’implementazione di tale tecnica riguardano essenzialmente l’introduzione di appositi registri o, analogamente a quanto fatto a suo tempo da Intel con la tecnologia MMX su interi, la condivisione di registri esistenti. Altre differenze riguardano il numero e le modalità di operazione delle unità dedicate alla esecuzione delle istruzioni SIMD. Vantaggi derivanti dalle tecniche SIMD sono ad esempio quelli del supporto dell’aritmetica di saturazione: se utilizzo la classica aritmetica di arrotondamento quando, ad esempio in un procedimento di incremento di colore, giungo al limite di rappresentazione di un dato un ulteriore incremento determina un overflow con il numero che raggiunge erroneamente il limite opposto. Quando io sommo invece ad esempio due valori a 32 bit che raggiungono un valore che eccede i limiti di rappresentazione avrei semplicemente bisogno di indicare che ho raggiunto il valore massimo rappresentabile. Questo è un evento particolarmente frequente in applicazioni multimediali (ad esempio nei valori di colore dei pixel) e l’aritmetica di saturazione serve appunto a questo. FRONT-END e BACK-END oncettualmente ogni processore può essere suddiviso in due blocchi principali. Il primo si occupa di prelevare le istruzioni dalla gerarchia di memoria e di predisporle alla esecuzione. Questo significa che le istruzioni devono essere decodificate (mediante suddivisione in microistruzioni o decodifica hardware) per renderle "comprensibili" dalle unità di esecuzione e quindi distribuite ognuna alla propria unità di esecuzione. Questo blocco è conosciuto come il Front End del processore. Naturalmente quante più istruzioni esso riesce a inviare alle unità di esecuzione tanto più è efficiente. Importante per questo stadio è la comunicazione con la gerarchia di memoria (quindi le modalità e la velocità con cui essa opera) per rifornire continuamente di istruzioni il Front End, e la predizione delle diramazioni che cerca di impedire che la pipeline del processore abbia stadi che non eseguono alcuna operazione in attesa che sia noto l’esito della condizione di diramazione. Il secondo stadio del processore è quello che esegue effettivamente il "lavoro" cioè esegue le istruzioni e fornisce i risultati. Questo stadio è chiamato Back End. L’efficienza di questo stadio è fortemente condizionata dal numero di unità di esecuzione di cui dispone (per cominciare l’esecuzione di più di una istruzione per ciclo) e dal tempo di cui necessita ciascuna unità per processare una istruzione. I processori moderni fanno uso dell’esecuzione fuori ordine proprio per fare fronte alla carenza di risorse (registri, unità di esecuzione) di cui generalmente soffrono. Questi due stadi influenzano reciprocamente le proprie prestazioni. Un Back End che esegue un elevato numero di istruzioni per clock deve essere continuamente rifornito di nuove istruzioni dal Front End da elaborare per non essere sottoutilizzato. Analogamente per facilitare il lavoro di distribuzione delle istruzioni alle unità di esecuzione del Front End le istruzioni devono essere eseguite efficacemente per liberare le risorse che questi deve alimentare e dirimere le diramazioni. Non è un caso se un processore come l’Athlon, che è dotato ad oggi probabilmente del più efficace Back End del mercato x86, sia stato fornito anche del Front End con il maggior numero, effettivo, di istruzioni decodificate per clock. Analogamente l’Itanium che, grazie alla definizione di una nuova architettura, riesce a decodificare e inviare alle unità di esecuzione ben 6 istruzioni per ciclo è stato dotato di massicce risorse di esecuzione. Un processore che non rispettasse questo principio di bilanciamento sarebbe destinato all’insuccesso. |
|
|
|
|
|
#7 (permalink) |
|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 682
![]() |
...continua...
IL SUPERAMENTO DEI LIMITI DELL'ARCHITETTURA x86 L’architettura x86 o IA-32 (Intel Architecture-32 bit) è vecchia di oltre 20 anni e sta mostrando, ora che le sue potenzialità sono state spinte all’estremo dalle recenti architetture, i propri limiti. Essi sono essenzialmente i seguenti: * Lunghezza variabile delle istruzioni: ciò comporta una fase di decodifica molto complessa che per compensazione richiede frequenze di lavoro più alte o pipeline più profonde con relativi problemi di predizione delle diramazioni. * Carenza dei registri: gli indirizzi dei registri nell’architettura x86 sono di soli 3 bit che consentono quindi l’indirizzamento di soli 8 registri di uso generale. I moderni processori hanno cercato di superare questo limite per mezzo di tecniche come la ridenominazione dei registri che aggiungono però ulteriore complessità. * Floating Point Stack: le istruzioni x87 usano per le proprie operazioni uno stack del quale viene fatto un uso intensivo con spreco di molti cicli per posizionare i dati occorrenti alla sua cima e che costituisce la ragione principale della sostanziale inferiorità delle CPU x86 rispetto le CPU RISC per la computazione floating point. * Istruzioni che referenziano indirizzi di memoria: con evidenti rallentamenti dovuti alle latenze associate ai chip di memoria in confronto a quelle che caratterizzano i registri interni della CPU (mentre le CPU RISC adottano un modello di memoria Load/Store che prevede le istruzioni operino solo tra registri e gli accessi alla memoria siano espliciti e deputati solo alle istruzioni di load e store). * Dimensioni del die: i buffer sempre più grandi, sempre più sviluppate tecniche di branch prediction (Intel stima che le penalità di errata predizione delle diramazioni comportino diminuzioni del 20%-30% delle prestazioni dei processori), la decodifica di istruzioni x86 in microistruzioni RISC sono tutti accorgimenti per estrarre grandi prestazioni dalle CPU x86 ma che richiedono molti transistor e quindi molto spazio sui die e quindi aumenti dei costi. La Intel ha ritenuto che, per assicurare l’incremento delle prestazioni che ha visto la recente storia del mercato dei PC, occorresse cambiare radicalmente il set di istruzioni anche a costo di penalizzare, naturalmente il meno possibile, la compatibilità con l’architettura precedente ma offrendo nel contempo prestazioni con le nuove architetture tali da giustificare un simile passo. La strada scelta da Intel per aumentare le prestazioni è quella di incrementare il parallelismo a livello di istruzioni (ILP). Le due strade privilegiate sino ad oggi per conseguire questo obbiettivo sono: la superscalarità nella quale il parallelismo non è esplicitamente indicato ma viene estratto mediante scheduling dinamico delle istruzioni in pratica deputando all’hardware tutto il lavoro (figura a sinistra) e la tecnica VLIW (Very Long Instruction Word) secondo la quale le istruzioni contengono esplicite informazioni riguardanti il parallelismo da esplicitare, ad esempio quante e quali istruzioni elaborare parallelamente che il compilatore raggruppa in pacchetti di dimensione fissata (in genere tra 112 e 168 bits inserendo NOP per riempire gli slots vuoti del pacchetto) e invia alle unità esecutive (figura a destra). In questo caso il grosso del lavoro è svolto dal compilatore e l’esecuzione è fissata staticamente al momento della compilazione. L’approccio usato da Intel e HP è una combinazione delle due per sfruttarne le caratteristiche di dinamicità e staticità di ognuna che è stato chiamato Explicitly Parallel Instruction Computing (EPIC). La filosofia EPIC in sostanza lascia decidere al compilatore quali istruzioni debbano essere eseguite in parallelo, le riunisce in un bundle (fascio) e manda in esecuzione questi bundle. In aggiunta a questo la tecnica EPIC prevede tipiche azioni di controllo e correzione della esecuzione come Prediction oppure Speculation (per dati e istruzioni) che consente il loro caricamento prima della risoluzione delle relative dipendenze per ridurre gli effetti della latenza di memoria. Le caratteristiche principali di questa architettura sono: * la possibilità di far collaborare il compilatore e l’hardware in modo che il compilatore possa controllare le risorse hardware in maniera più diretta anche a livello di predizione delle diramazioni o della gestione della gerarchia di memoria. Un tipico esempio di questa collaborazione è la tecnica di Predication che consente ad esempio l’esecuzione in caso di una diramazione IF THEN ELSE di entrambi i THEN e ELSE (che non dipendono uno dall’altro) in parallelo (e quindi con la latenza di uno solo) salvo poi invalidare quello non corretto mediante apposito valore dei flag di Predicate assegnati a ciascuna delle diramazioni all’atto della loro esecuzione; * il grande numero di registri utilizzabili che, oltre ad una computazione sia di interi che floating point più agevole specie a doppia precisione, fornisce adeguate risorse hardware per funzioni specializzate come la Predication e consente grande flessibilità ed estensibilità circa la superscalarità dei chip basati sul ISA IA-64 (è difficile per un processore x86 gestire un numero elevato di pipeline con solo 8 registri indirizzabili). Inoltre è possibile ridurre gli accessi alla memoria solo alle operazioni load e store sfruttando i registri per i risultati intermedi; * grande memoria indirizzabile grazie ai registri a 64 bit che consente di superare gli attuali 4 GB dell’architettura IA-32, i cui limiti peraltro non sono ancora stati raggiunti dalle applicazioni attuali ma potrebbero esserlo in futuro, e di supportare numerosi GB di RAM; |
|
|
|
|
|
#8 (permalink) |
|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 682
![]() |
...continua...
Il problema della filosofia EPIC risiede esattamente nel coinvolgimento del compilatore e nella definizione di un nuovo ISA per cui la esecuzione di codice IA-32 risulta problematica ed inoltre comporta, senza un adeguato e costoso supporto Intel, un certo ritardo nello sviluppo di software che possa avvantaggiarsi di questa architettura. Inoltre l’esecuzione in ordine e il raggruppamento statico delle istruzioni tipiche delle CPU EPIC possono poco contro i miss della cache che sono poco prevedibili a livello di compilazione e per alcune applicazioni come ************************ non è detto che il tempo speso in fase di compilazione possa essere adeguatamente compensato dai tempi di esecuzione. Thread Level Parallelism (TLP) Un’altra strada a disposizione degli sviluppatori per incrementare le prestazioni sfruttando il parallelismo consiste nel Thread Level Parallelism (TLP) cioè nella esecuzione in parallelo da parte di sistemi multiprocessore che possono essere fisici (cioè con la presenza effettiva di più CPU sul medesimo chip) o virtuali (cioè ottenuti da un singolo chip che emula un sistema multiprocessore simmetrico). Questo approccio trova le sue ragioni e naturali applicazioni nell’ambito del mercato server dove effettivamente molte applicazioni possono essere effettivamente scomposte in più threads e assegnati ai diversi processori che costituiscono il sistema. In ambito desktop attualmente l’implementazione del TLP ha un impatto assai contenuto ma le applicazioni multimediali sono naturalmente orientate al TLP e quando nuovi sistemi operativi, come Windows 2000 e XP che supporta sistemi multiprocessore, o linguaggi di programmazione come ************************, che predispone a sviluppare applicazioni multithread, diventeranno standard le cose potrebbero sensibilmente cambiare. L’approccio TLP in sostanza pone la sua attenzione sul design di sistema più che non sulla definizione di un nuovo ISA (Instruction Set Architecture) ed in particolare, dovendo prevedere la condivisione di risorse tra i diversi processori, sulla banda di memoria. il TLP prevede lo switch delle delle risorse del chip dal thread attualmente in esecuzione ad un nuovo thread, quando il primo inizia un'operazione a lunga latenza. Ciò riduce le probabilità di stallo della lunga pipeline, permettendo al secondo thread di essere eseguito mentre viene completata l'operazione a lunga latenza del primo. Il dirottamento delle risorse da un thread all'altro comporta, comunque, una penalizzazione delle prestazioni, visto che le istruzioni del thread corrente devono essere drenate dal pipeline. Poichè lo stato dell'architettura del thread deve essere conservato nel pipeline, il nuovo processore logico deve essere attivato, e le istruzioni dal nuovo thread devono essere fornite alle risorse del processore. Con HT, comunque, le applicazioni software adattate al multiprocessore possono essere lanciate col doppio dei processori logici da usare. Ogni processore logico può rispondere agli interrupt in modo indipendente. Il primo processore logico può seguire un thread, mentre il secondo può seguirne un altro simultaneamente. Siccome i due thread condividono un set di risorse d'esecuzione, l'HT può usare risorse che sarebbero altrimenti inutilizzate se fosse eseguito un solo thread. Il risultato è un aumento dell'utilizzazione delle risorse d'esecuzione in ogni unità di processore fisico. Ad esempio, un processore logico può eseguire un'operazione floating-point mentre l'altro processore logico esegue un'operazione di addition o di load. HT è complementare ai sistemi MP, perchè il sistema operativo è in grado non solo di registrare thread separati ed eseguirli su ogni processore fisico simultaneamente, ma può farlo anche su ogni processore logico, sempre in simultanea. Tutto questo migliora le prestazioni generali e la risposta del sistema, perchè molti thread paralleli possono essere distribuiti prima grazie al doppio dei processori logici che il sistema ha a disposizione. Anche se a disposizione c'è il doppio dei processori logici, questi continuano a dividersi le stesse risorse d'esecuzione: così, il beneficio che deriverebbe da un altro processore fisico con le sue proprie risorse offrirebbe i più alti livelli di performance. In altre parole, HT è complementare al multi-processing, offrendo un miglior parallelismo in ogni processore del sistema, ma non è un rimpiazzo per il dual o il multi processing. Code Level Parallelism (CLP) Questo livello di parallelismo riguarda la possibilità di eseguire in uno stesso istante più istruzioni distinte. In questo caso, l'approccio è quello delle architetture superscalari, ovvero dotate di due o più unità di esecuzione funzionanti in parallelo, in modo da poter mandare in esecuzione più istruzioni contemporaneamente, nell'ipotesi naturalmente che non vi sia una dipendenza tra queste istruzioni, ovvero che il risultato di alcune non serva come input per le altre. Questo problema di rimozione delle dipendenze, per poter sfruttare al massimo le architetture superscalari, è molto sentito, e c'è gente che vi dedica l'intera carriera... Instruction Level Parallelism (ILP) Riguarda la capacità di una singola istruzione della CPU di elaborare contemporaneamente più dati. Questo livello di parallelismo si incrementa con l'uso delle istruzioni SIMD (Single Instruction Multiple Data). Intel ha ereditato queste istruzioni dal mondo dei DSP (Digital Signal Processor) che tipicamente svolgono compiti dove è necessario avere istruzioni di questo tipo per ottenere prestazioni elevate. Nei processori x86, le istruzioni SIMD sono le MMX, le SSE/SEE2/SSE3, le 3DNow! e le 3DNow! Professional (a seconda della CPU che consideriamo). L'uso e l'efficacia di queste istruzioni sono in funzione della possibilità di trattare i dati in modo parallelo: alcune applicazioni non potranno mai beneficiare di queste istruzioni. Per sfruttare maggiormente l'ILP sono state sviluppate particolari famiglie di processori e di compilatori. Ci sono 2 tipi di tecniche per sfruttare l'ILP: 1) STATICHE: sono tecniche messe in atto dal compilatore stesso nella fase di "produzione" del linguaggio macchina; queste tecniche, tanto per citarne alcune sono la schedulazione statica (per i dati), la diramazione ritardata e la predizione statica (per i controlli). 2) DINAMICHE: queste si rendono operative al momento dell'esecuzione del programma. FINE |
|
|
|
|
|
#10 (permalink) |
|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 682
![]() |
![]() Però prima era su un messaggio solo; probabilmente è un impostazione del forum; ho visto che adesso c'è il limite massimo di 10000 caratteri per messaggi e di minimo 10 caratteri. Ciao!
|
|
|
|
![]() |
| Strumenti discussione | |
|
|
Discussioni simili
|
||||
| Discussione | Autore discussione | Forum | Risposte | Ultimo messaggio |
| Guide: Reti di PC | Filippo1974 | Sezione hardware | 3 | 07-12-2007 11.33.58 |
| Guide: Audio nei PC | Filippo1974 | Sezione hardware | 2 | 03-12-2007 10.44.33 |
| Guide: GPU | MarcoGT | Sezione hardware | 1 | 29-11-2007 19.25.45 |
| Guide: Cache | MarcoGT | Sezione hardware | 0 | 29-11-2007 19.13.44 |
| Guide: Pipeline | MarcoGT | Sezione hardware | 0 | 29-11-2007 19.08.29 |