Calcolatori elettronici/Le architetture a pipeline
Per migliorare le prestazioni del processore, oltre ad aumentarne la frequenza, si può modificarne l'architettura.
I processori si differenziano in base al tempo, in termini di colpi di clock, richiesto per il completamento delle istruzioni:
- CISC: (fino agli anni '70) per completare un'istruzione è necessario un certo numero di colpi di clock;
- RISC: (anni '80) completano un'istruzione in un solo colpo di clock;
- superscalari: (anni '90) a ogni colpo di clock completano più di un'istruzione.
Architettura RISC
[modifica | modifica sorgente]I processori con architettura RISC fanno uso delle pipeline.
Nelle istruzioni si possono identificare delle fasi standard (per es. Fetch, Decode, Operate, Write), ciascuna delle quali opera in un solo stadio del processore per colpo di clock → i vari stadi possono essere fatti lavorare in parallelo, così mentre viene completata un'istruzione ce n'è già un'altra a cui manca una fase per il completamento, e il primo stadio inizia a eseguire un'altra istruzione ancora → a ogni colpo di clock viene completata un'istruzione.
Gli stadi sono inframezzati da registri, sincronizzati dallo stesso segnale di clock.
Instruction set
[modifica | modifica sorgente]Il set di istruzioni deve comprendere istruzioni semplici, e le istruzioni devono avere un codice operativo di dimensione regolare in modo che la decodifica sia veloce. C'è un unico modo di indirizzamento, che si basa su un registro e una costante.
Tutte le istruzioni lavorano sui registri, tranne le istruzioni LOAD
e STORE
che rispettivamente leggono e scrivono tra la memoria e un registro. In questo modo si evitano istruzioni che effettuano accessi pesanti alla memoria. I RISC dispongono di un numero significativo di registri (~100).
Tipicamente l'unità di controllo è cablata e non microprogrammata, grazie al fatto che la gestione dell'unità di elaborazione è più semplice. Un'unità di controllo cablata è inoltre più veloce e più piccola di una microprogrammata.
Stalli
[modifica | modifica sorgente]Tutte le istruzioni devono passare attraverso le stesse fasi; non sono ammessi dei fetch che richiedono più colpi di clock, ma ogni fase deve durare un solo colpo di clock.
Possono verificarsi dei casi imprevedibili, detti stalli, in cui non si fa in tempo a eseguire una fase entro un singolo colpo di clock (ad es. fetch).
I ritardi del fetch possono essere attenuati con una coda delle istruzioni subito dopo la fase di fetch, che normalmente è piena. Se si verifica un miss, lo stadio di decodifica continua a lavorare sulle istruzioni rimaste nella coda.
Vincoli di dipendenza
[modifica | modifica sorgente]Se alcune istruzioni richiedono di operare sul risultato dell'istruzione precedente, oppure le istruzioni in esecuzione in parallelo condividono degli operandi:
- soluzione hardware: il processore aspetta che l'istruzione precedente finisca di accedere al dato condiviso;
- soluzione software: il compilatore aggiunge delle istruzioni
NOP
→ l'hardware è più semplice.
Istruzioni di salto
[modifica | modifica sorgente]Nella pipeline vengono caricate le istruzioni in modo consecutivo → quando l'istruzione di salto scrive il nuovo valore nel PC, l'istruzione consecutiva nel frattempo è già stata caricata in pipeline:
- soluzione software: il compilatore aggiunge delle istruzioni
NOP
in fase di compilazione; - soluzione hardware: il processore deve annullare l'esecuzione delle istruzioni in pipeline da saltare.
Salti condizionati
[modifica | modifica sorgente]I salti condizionati presentano un'ulteriore complicazione, perché non è detto che modifichino il PC.
Già in fase di decodifica dell'istruzione, il processore può tentare di fare una predizione, per evitare il più possibile di caricare inutilmente istruzioni, attraverso opportuni algoritmi basati sull'analisi del codice e/o su statistiche dinamiche calcolate in fase di esecuzione (ad es.: la volta precedente l'istruzione ha saltato?).
Vantaggi
[modifica | modifica sorgente]Un codice ottimizzato per i processori RISC risulta più veloce e di dimensioni comparabili rispetto all'alternativa CISC, perché le istruzioni eseguite nonostante siano in numero maggiore sono più semplici sia in termini di colpi di clock sia in termini di lunghezza in byte. Un codice compilato per un particolare processore RISC però è poco flessibile in termini di compatibilità con altri processori RISC → per questo motivo all'inizio non vennero utilizzati molto in ambito general-purpose ma solo in ambito special-purpose. I processori general-purpose odierni invece usano comunque le pipeline, grazie a dell'hardware che traduce al volo le istruzioni generiche dell'assembler 8086 ad istruzioni più semplici di tipo RISC.
Nei RISC la latenza, ovvero il tempo medio di attesa per il servizio di un interrupt, è molto bassa perché a ogni colpo di clock viene completata un'istruzione.
Processori superscalari
[modifica | modifica sorgente]I processori superscalari usano due pipeline in parallelo per migliorare le prestazioni: a ogni istante sono in esecuzione 8 istruzioni in parallelo. Svantaggio: i vincoli di dipendenza raddoppiano.
Le unità di fetch, decodifica e write-back elaborano due istruzioni per colpo di clock. La fase di operate è svolta da varie unità differenziate a seconda del tipo di istruzione: unità per l'aritmetica intera, unità per l'aritmetica in virgola mobile...
Completamento non-in-ordine
[modifica | modifica sorgente]L'unità di decode diventa un'unità di smistamento (o di issue) che distribuisce le istruzioni sulle varie unità di operate, in modo che tutte le varie unità lavorino il più possibile continuamente → le istruzioni non vengono eseguite nell'ordine in cui compaiono nel codice, ma in parallelo → la coda di completamento usa degli algoritmi che riordinano i risultati e ne garantiscono la correttezza.
Processori multithread
[modifica | modifica sorgente]Non si può aumentare troppo il numero di stadi in parallelo, perché i vincoli di dipendenza sarebbero troppi → si passa dall'Instruction Level Parallelism (ILP) al Thread Level Parallelism (TLP):
- fino agli anni '90, il processore adottava il time sharing, cioè a ogni programma in esecuzione il processore dedicava una parte del tempo, ma in ogni istante eseguiva un solo programma alla volta;
- un processore multithread esegue in parallelo istruzioni provenienti da più programmi in esecuzione → sfruttano al massimo le unità di operate.
Processori multicore
[modifica | modifica sorgente]All'interno dello stesso circuito sono integrati più processori, detti core, su cui viene distribuito il lavoro.