L’adozione della pipeline punta a migliorare le prestazioni di un processore modificando l’architettura dello stesso. Grazie all’introduzione della pipeline, se a pieno regime, i RISC riescono a produrre un risultato per ogni colpo di clock, mentre quelli superscalari eseguono più di un’operazione per colpo di clock. La pipeline permette di eseguire più operazioni in parallelo; questo permette di migliorare di n volte le prestazioni di un processore nel caso sia presente una pipeline di n stadi in cui tutti gli stadi impiegano lo stesso tempo a eseguire una operazione. Ogni istruzione può essere quindi divisa in parti, che approssimativamente impiegano lo stesso tempo ad essere eseguite:
• Fetch, l’istruzione viene caricata
• Decode, l’istruzione viene decodificata e vengono attivati i corrispondenti segnali di controllo e prelevati gli operandi
• Operate, l’istruzione viene eseguita
• Write, il risultato viene salvato
Tra uno stadio e quello successivo è presente un buffer con lo scopo di ritmare le operazioni. Molto spesso ciascuno stadio impiega più di un colpo di clock, quindi ci si trova in una situazione di stallo: uno stadio non trova il buffer successivo libero di essere scritto e quindi è costretto a fermarsi, bloccando anche tutti gli stadi a monte; quelli a valle possono continuare a lavorare ma si ha una perdita delle prestazioni e la creazione di una bolla di inattività. Le principali cause di stallo sono la lentezza di alcune operazioni di caricamento e scrittura o dall’esecuzione di alcune operazioni difficili da realizzare. La lentezza dei caricamenti è in parte arginabile con l’utilizzo di cache molto veloci e affidabili.
Quando si ha un’operazione di fetch più lunga del previsto l’intera pipeline viene bloccata, quindi per cercare di diminuire la possibilità che questo si realizzi, il buffer interposto tra fetch e decode è strutturato come una coda FIFO: viene fatto il fetch di più istruzioni consecutive ancora prima che esse vengano effettivamente eseguite. Nel caso di istruzioni di salto la coda viene svuotata e viene ririempita con i nuovi indirizzi ricalcolati. Il responsabile di queste operazioni aggiuntive può essere:
• Soluzione SW: Il compilatore mette NOP oppure sposta alcune operazioni
• Soluzione HW: l’HW controlla e corregge. A volte è in grado di “prevedere” con che probabilità il salto avverrà. Esiste predizione statica, se la predizione è fatta sulla base del codice che sta arrivando, dinamica se viene ottenuto in base all’utilizzo di flag e del loro stato. Nel caso si stiano effettuando calcoli su risultati “previsti” e non certi il processore salva i risultati in appositi registri (esecuzione speculativa).
o Salti incondizionati: può essere che il circuito che opera il fetch sia in grado di riconoscerli e di caricare direttamente il dato corretto (brach folding)
o Salto condizionato: è necessario che si abbia il risultato del controllo della flag prima di procedere.
Per impedire la formazione di errori è necessario che più dati che vengono processati in parallelo nella stessa pipeline non abbiano dipendenza tra di loro:
• Soluzione HW: è l’HW a riconoscere la presenza di dipendenze, introduce dei periodi di stallo per aspettare che gli operandi vengano prodotti
• Soluzione SW: è il compilatore che aggiunge delle NOP in modo da garantire che l’operato venga prodotto prima di essere utilizzato. Alcune volte il compilatore sposta alcune istruzioni prima delle altre in modo da sostituire le NOP con altri operazioni (Delayed Load)
Per le immagini Ringraziamo ©Nato vs Warsaw
| < Prec. | Succ. > |
|---|






