| La
ribellione allo standard : nascita del RISC |
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:
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!!
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 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,
ma questo è un'altra storia che avrò piacere di trattare in
un'altra occasione;-).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.
Delle pipeline parleremo comunque estensivamente in un prossimo
articolo.