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! Confusi? 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. 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 virgola
mobile). Infine: abbiamo detto che la macchina di Von Neumann
, che rappresenta il nostro processore CISC, è un pò come un
tizio con un solo braccio. 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 (a
tal proposito, date un'occhiata al mio
articolo sulla SOI). È tutto vero, ovviamente, ma la
domanda resta senza risposta. Come fanno gli ingegneri di AMD
a stabilire sulla carta che un dato processore può toccare certe
frequenze ? Siete ragazzi svegli, e avete intuito che la risposta
sta proprio nel titolo del paragrafo: 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! 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è fra poco vedremo
che 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.