Sei sulla pagina 1di 8

Cosa cambia nel momento in cui vogliamo considerare un processore che fa anche elaborazione

su dati fp, tenendo presente che un dato fp ha un'aritmetica completamente diversa da quella che
avviene sui dati interi.
Il floating point fa spostare la virgola in modo da averla posizionata a sinistra( anche a destra pi
frequente) della cifra pi significativa, dopo di che il numero moltiplicato per un certo esponente
che va, necessariamente, anche lui scritto nel numero. Alla fine ho un numero che comprende un
campo che si chiama esponente, l'altro mantissa pi il bit di segno che mi dice se il numero
positivo o negativo. Abbiamo un bit di segno esplicito per la mantissa ed implicito per lesponente
( sar un numero complemento a 2, la mantissa in binario puro). Se dobbiamo fare una
moltiplicazione tra questi dati bisogna fare la somma degli esponenti dei due fattori e fare il
prodotto delle due mantisse, dopo di che dobbiamo normalizzare le mantisse. Se questo prodotto
genera dei bit di over flow, non saranno persi, saranno considerati, ma diminuir i bit per il prodotto
degli esponenti. Quindi vediamo come fare unoperazione del genere comporta un certo costo
Somma, prodotto, devo fare una logica; per la divisone analoga. Invece per la somma non devo
fare la somma tra le 2 mantisse, ma vanno prima allineate allo stesso esponente. Si confrontano
gli esponenti, la differenza degli esponenti, il numero con exp minore si shifta a destra della
differenza degli esponenti, sommeremo le 2 mantisse dopo averle allineate e osserveremo come
esponente quello del maggiore. Se poi nella somma c stato un over flow o under flow, quindi
sono scomparse delle cifre significative, potr spostare il numero a sinistra diminuendo
lesponente. Una somma o differenza si fa confrontando gli esponenti, shiftando la mantissa del
numero che ha exp minore e poi loperazione tra le mantisse. Questa logica leggermente pi
complessa di quella vista sino ad ora e, se volessimo realizzarla in hardware, avremmo delle
latenze differenti Devo integrare in un processore che vuole fare operazioni fp, una parte che
faccia calcoli sui fp e posso ragionare in 2 maniere diverse. O un unico blocco che presenta al suo
interno una serie di blocchetti ( la nostra ALU), oppure nella seguente maniera ( diverso da prima):

Se dovessi fare un oggetto che integra il disegno sopra non tra le migliori scelte. Se c una
coppia che vuole fare un prodotto, lo fa ma blocca tutto fin quando non arriva unaltra coppia che
vuole fare qualcosaltro, questo per fare fin quando non arriva dura pi di un colpo di clock
L'operazione fp dura pi di un colpo di clock. Siccome un calcolo fp, essendo pi lento rispetto
quello fatto su aritmetica intera e dura di pi possiamo o ragionare nel modo tale che il colpo di
clock diventa lungo quanto la latenza massima di questo tipo di operazioni. Se il divisore fp 20
volte pi lento dellALU, possiamo prender il colpo di clock e farlo durare 20 in modo tale che la
divisione verr fatta in un solo colpo di clock, anche se tutte le altre cose verranno fatte in colpi di
clock molto pi lunghi di quelli strettamente necessari. Ogni fase durer 20 secondi. Allora
decidiamo di procedere in unaltra maniera: manteniamo il colpo di clock per quella durata che
andava bene a risolvere le problematiche legate alla pipeline di un processore a virgola fissa, e
quando avremo caricato le virgole mobili, faremo si che quel dato duri pi colpi di clock.
Si comincia a pensare a delle operazioni che hanno una fase EX multiciclo Il colpo di clock
rimasto lo stesso, ma siccome per forza di cose necessito di pi tempo allora durer pi colpi di
clock. Nelle operazioni dove non abbiamo a che fare con op fp potremo continuare il nostro lavoro
con dei colpi di clock in tempi contenuti e completare quelle istruzioni. Nel momento n cui un
processore deve fare unoperazione che divisione tra 2 fp, ci sar la fase EX che durer 20 colpi

di clock. Questo fa pensare che tenere gli oggetti bloccati ( se ne sto usando uno gli altri non posso
sfruttarli) non una cosa intelligente: se dura 20 colpi di clock non ha senso, in caso di
moltiplicazione, se la dovessi fare non potrei. A livello di aritmetica fp gli elementi di calcolo sono
tra loro svincolati, ognuno fa vita a se. Se devi fare una divisione, inizio e se la prossima vuole fare
un prodotto pu usare il moltiplicatore e non come prima che troviamo lALU impegnata. Possiamo
pensare un processore fp, dove gli elementi di calcolo sono svincolati, e se lo sono, dobbiamo
avere un sistema di accesso che permetta un uso svincolato di questi elementi. Per cui non posso
avere un latch uscita ALU, un latch A, un latch B, ma avr un BUS che porter 2 operandi a uno
dei 2 oggetti e l'uscita, con un meccanismo di BUS, andr a scriversi nel registro indicato durante
la fase di WB.
Abbiamo un certo numero di elementi, la loro latenza( tempo che perdo a fare il calcolo) sar
diversa. Rinunciando all'approccio semplicistico di dilatare il colpo di clock di tutto il sistema, in
base alla latenza pi lenta di questi elementi che ci porterebbe ad andare in maniera sottoutilizzata
su tutte le altre parti del processore, continuiamo a tenere 1 colpo di clock per quello che avevamo
trovato ( latenza memoria al posto dellALU) e ,in base a questo colpo di clock, strutturiamo
l'hardware per operare con un numero di colpi di clock maggiore.

Questo numero sar differente secondo le operazioni. Le operazioni multiciclo (hanno


lunghezza e durata diversa) creano delle complicazioni non banali: se ho un'istruzione che
produce X = Y + Z ( dati fp) piuttosto che una funzione che produce X= Y/Z (dati non fp), le
operazioni sono diverse nel momento in cui il valore di X deve essere usato da un'istruzione
successiva. Il risultato sar disponibile 4 colpi di clock o 24 ( se ipotizziamo che la durata
delloperazione fp sia 24 e laltra 4) della sua fase di EX. Mentre con le operazioni che calcolavano
dei risultati con l'ALU, come abbiamo sempre visto, e i risultati usati venivano prodotti da altre
istruzioni, che poi cortocircuitavamo allingresso dell'ALU e non avevamo problemi di alcun tipo,
l'unico si presentava quando il dato lo dovevamo prendere dalla memoria ( ricordiamo che questo
era disponibile solo a partire della fine della fase MEM Dovevo necessariamente inserire uno
stallo). In una situazione del genere ( processore fp), il dato verr prodotto solo dopo 24 colpi di
clock, il che implica necessariamente il presentarsi di un problema Abbiamo una gestione degli
stalli per conflitto di dati estremamente pi complicata, perch la nostra X la avremo disponibile
dopo un tot di colpi di clock. Diventa importante, quando si fa uso delle istruzioni fp, avere delle
istruzioni che, nel momento in cui c' una particolare istruzione che produce X, e una che la

consuma, farebbe comodo avere tra queste 2 istruzioni per completare il calcolo dell'istruzione( la
X) stessa.
Se queste istruzioni non ci sono, dovr necessariamente stallare il sistema. Vedremo, comunque,
che ci saranno delle tecniche di schedulazione per aiutare il processore a non stallarsi ( attuate
dal compilatore con il solo scopo di trovare delle istruzioni da inserire per non far stallare il
processore). Se la schedulazione non possibile il processore si dovr stallare.
Dal momento in cui abbiamo una situazione in cui ho supposto che devo stallare, c la possibilit
di stallare unistruzione e, di conseguenza, tutte quelle successive ( se le altre fossero andate
avanti e si stallava solo quella che creava lo stallo, avrei avuto: 1 un conflitto strutturale e 2 un
problema legato allutilizzo dei dati a partire dalle istruzioni successivi). Questo ragionamento
appena esposto vale anche per i fp ma potrebbe essere dannoso dal punto di vista delle
prestazioni (se dura 20 colpi di clock e blocco tutte le successive sto bloccando il calcolo per un
lasso di tempo considerevole) del sistema. Laltra possibilit stallare la singola istruzione che
crea problema e, difatti, quello che avverr a livello di gestione di un processore fp. Si avr la
possibilit di stallare l'istruzione, ma solamente lei.
Quando abbiamo parlato di uno stallo in aritmetica fissa, abbiamo visto che dovevamo stallare
anche tutte quelle che seguivano Le fasi si trovavano in conflitto strutturale. E bene osservare
che, quello che stavo bloccando, era un sistema basato su 1 solo colpo di clock. Ora la situazione
tale che il blocco che viene effettuato, potrebbe essere anche di 20 colpi di clock, che hanno
unimportanza ben diversa rispetto 1 colpo di clock e, per questa motivazione, si pensa di stallare
una sola istruzione e non tutte le altre. Se si riesce a fare ci, abbiamo che il programma va avanti.
Ovviamente c' un costo, perch devo valutare che non ci siano altre istruzioni, nel caso in cui ne
abbia bloccato una e voglia andare avanti, che usano il risultato dell'istruzione che sto stallando In
quel caso anche quelle dovranno essere stallate. Dovremo anche fare in modo che, se l'istruzione
sta producendo X e l'istruzione che sto stallando produrr Z, se esiste unistruzione che usa A e B (
per esempio) per produrre qualcosa che dovr essere sovrascritto in Z, anche lei dovr essere
stallata se no rischia di: finire prima, calcolare Z, scriverlo e poi il Z, che sar a rigore di logica pi
recente verr sovrascritto quando laltra istruzione terminer. Dobbiamo avere, perci, un
meccanismo che permetta che le istruzioni vengano completate in maniera fuori ordine
Nellaritmetica intera, quando bloccavo unistruzione, bloccavo tutte le altre e quando riprendeva,
riprendevano le altre terminandole nellordine in cui mi arrivavano. Qui, stallando solo una
particolare istruzione, pu verificarsi che una di queste, termini dopo che una iniziata dopo di lei.
(pu avvenire anche in assenza di stalli). Il tutto porta ad una gestione degli operandi con un
algoritmo che prende il nome di algoritmo di Tomasulo. Cosa fa? Usa una serie di tabelle,
all'interno del processore,( per essere pi precisi si trovano dentro la CU del processore) che
gestiscono, come semafori, l'accesso ai dati e alle risorse. Sostanzialmente se un'istruzione
vuole usare il moltiplicatore e, per usare il moltiplicatore necessita di 2 operandi di cui uno deve
essere prodotto dall'istruzione precedente, non ha senso farla entrare e bloccare il processore, ma
converr farla entrare solamente quando i suoi operandi saranno disponibili.
Se unistruzione deve fare una moltiplicazione e si trova nella fase M2 pu utilizzarsi il
moltiplicatore da parte di unaltra istruzione?( riferito a immagine sopra) Le unit per il calcolo
possono, anche loro, essere gestite in pipeline e se un'altra coppia le vuole usare potr
tranquillamente farlo: l'unico vincolo presente all'ingresso e all'uscita ( il BUS). Solo queste due
operazioni ( accesso e uscita) non sono simultanee.

Dal punto di vista dell'architettura non basta "aggiungere" i blocchi, ma servono anche dei registri
che permettano di contenere i dati fp. In maniera superficiale si pu pensare di avere gi i registri
che al posto di contenere 32 dati interi conterranno, a seconda delloccorrenza, 21 dati intera e 11
fp ( per esempio). In realt ci non ha molto senso, perch avevamo che quei registri, che
conosciamo, sono collegati tramite il latch A e B allALU. Se abbiamo che uno qualsiasi di questi
registri pu essere mandato sia in A che in B, e iniziamo a dire che uno qualsiasi di questi registri
pu contenere un dato fp che deve essere mandato sul BUS che va in ingresso al
moltiplicatore/divisore/ecc. fp, siamo facendo qualcosa che appesantisce inutilmente larchitettura.
Per esempio se dico che R6 pu contenere, a seconda dellesigenza, un dato fp o intero, deve
avere una logica per andare a destra, ma anche sotto ( nella figura sopra ) tenendo presente che
uno di quei dati, o meglio una certa variabile, nella sua vita, non potr essere intera e fp
contemporaneamente. Una variabile, in un programma, nasce in una maniera e cos muore. Non
ha senso che un fp user A e B per un calcolo intero, cosi come un intero non user sommatore,
moltiplicatore, ecc. fp. Quando faccio una customizzazione della variabile, di fatto ne sto
creando unaltra ( la variabile di rango maggiore viene customizzata a fp). Ha pi senso pensare
a un banco di registri fp collegato alle sue unit di calcolo fp, mentre il banco dei registri sar
collegato allALU. Ci sar la possibilit di conversare tra i 2 registri, perch, alle volte, sono
necessarie delle conversioni di dati. Se vogliamo fare unoperazione di questo genere, dovremo:
leggere da qualche parte nel registro R quanto vale lintero X e convertirlo in fp; la sua versione fp
sar copiata in uno dei registri fp e da li si acceder allelemento. Esiste, tra i due mondi, una
specie di dogana. Dal punto di vista strutturale, un processore che usa aritmetica fp, avr dei
registri che possono contenere fp, connessi con un meccanismo di BUS ad una struttura che fa le
operazioni. Questa struttura mantenuta in maniera dicotomica (no fp) da una parte e individuale
(fp) nellaltra.
In questo sistema descritto, abbiamo la necessit di gestire una serie di problematiche: stalli che
non avevamo nellaritmetica intera ( quelli che avevamo indicato come: RAW, WAR, WAW, RAR).
Avevamo visto che, lunico che causava problemi, era la RAW, (non potevamo aspettare che
listruzione precedente lo scrivesse nella WB, perci lo si andava a prendere, per risparmiare
tempo, in ALUoutput o LMD) ma ora anche WAW pu causare dei problemi: se abbiamo 2
istruzioni come una divisione che vuole scrivere X, unaltra che vuole scrivere X come somma, e
se la somma viene dopo la divisione, lei, se le cose non sono soggette a controllo, andr a
scrivere X prima che, quella prima di lei, labbia scritta. Vediamo i vari casi con esempi:
WAW: Se X= Y*Z avviene prima di X= A+B, la prima dura 24 colpi di clock e non ci sono altre
istruzioni tra loro 2, allora la 2 scriver prima della 1 operazione.
WAR: Y= X+Q, se Q deve essere ancora prodotto stallo listruzione. Nel frattempo, X= A+B,
continua e quando finisce scriver X. Quindi, se stallo la 1 dovr stallare anche l'altra.

RAW: Esisteva gi nel caso intero e continua a esistere nel caso fp. Abbiamo un'istruzione che
scrive X= Y+Q e un'altra Q= X/B. Siccome questultima deve leggere X dopo che una precedente
lha scritto, dovr aspettare.
Infine abbiamo la RAR: falso problema anche in questo caso Nessuno dei due modifica niente.
Con problematiche di questo genere lo studio degli stalli inizia a essere delicato. Nell'aritmetica
intera conflitti strutturali non se ne avevano mai. Conflitti di dato, qualche volta, ma con la corto
circuitazione si creava lo stallo solo in fase di LOAD. Ora abbiamo visto esserci conflitti di memoria
(prelevo un dato o istruzione, non la trovo in cache) e una serie di stalli legati alle operazioni, dove
queste operazioni sono diverse anche dal punto di vista della penalizzazione. In che senso?
Abbiamo una penalizzazione legata alle operazioni di somma, sottrazione conversione; una
penalizzazione legata ai confronti, ma si differenziano specialmente quelle legate a moltiplicatori e
divisori.
Le divisioni sono la causa del maggior numero di stalli e a seguire la moltiplicazione. Non
dobbiamo pensare solo al fatto che la divisione pi lenta; fortunatamente anche l'operazione
meno frequente negli algoritmi rispetto alla somma o prodotto.
Queste sono le fasi che intervengono nel momento in cui abbiamo la gestione del processore fp in
cui la fase di fetch dellistruzione e quella di accesso ai dati sono a sua volta suddivise in una serie
di sotto fasi Sto cercando di ridurre il colpo di clock al minimo, si ridotto mantenendosi integro
nel calcolo intero, mentre nella MEM e memoria istruzioni sono state un po dilatate.

Vediamo l'accesso agli elementi strutturali.


E direttamente connesso al banco dei registri (fp). Abbiamo una serie di elementi, tra cui 2
moltiplicatori, un divisore, un sommatore, ununit di calcolo intera. Come avviene il meccanismo di
accesso a questi elementi? I dati vengono inviati in ingresso agli oggetti che ne fanno il calcolo, i
risultati vanno a scriversi nel banco dei registri e abbiamo un elemento di logica che controlla i
registri nel senso se quei registri contengono effettivamente il dato che si aspetta che essi
contengano Non sono dei registri che non contengono ancora il risultato perch deve essere
ancora prodotto. Lelemento di logica si chiama scoreboard . Perch abbiamo 2 moltiplicatori? Per
ottimizzare il numero di stalli che si avrebbe nel sistema stesso. Se voglio ottimizzare le

prestazioni, e ho spazio nel processore, aggiungo dell'hardware. Potrei mettere il divisore dato che
abbiamo detto che sono queste le operazioni pi lente, ma non convenire perch sono meno
frequenti e gli stalli strutturali delle divisioni sono pochi. In pi, dal punto di visto hardware, il pi
complesso e costoso. Il sommatore veloce e presenta un hardware molto semplice, non c
bisogno di aggiungerne altri. Rimane, e conviene, il moltiplicatore che ha una hardweristica pi
semplice ed pi frequente Ha senso, quindi, affiancare 2 moltiplicatori.
Schedulazione
Dal momento in cui abbiamo la necessit di reperire istruzioni da schedulare tra ,unistruzione che
produce un risultato, e unaltra che la va a usare, c' bisogno di trovare, nel listato, le istruzioni che
si possono intermezzare tra chi produce il risultato e chi lo usa. Se queste istruzioni, in numero
devono essere considerevoli, un algoritmo scritto in maniera classica difficilmente d agio al
compilatore di trovare queste istruzioni. Esistono 2 tecniche: una di queste lo srotolamento del
loop ( loop unrolling). Supponiamo di voler fare il prodotto di 2 vettori: abbiamo il vettore X e il
vettore Y che, moltiplicati, danno Z che verr, a sua volta, scritto in memoria.
Supponiamo che X caricato in memoria a partire dall'indirizzo 1000, Y a partire 2000 e Z dovr
essere scritto a partire dall'indirizzo 3000. ( In blu ci che aggiungo dopo leggendo sotto)
ADDI R1 800 Ro
LOAD F1 R1 1000
LOAD F2 R1 2000
MULT F3 F1 F2
STORE F3 R1 3000
ADDI R1 R1-8
BNEZ R1-6
Supponiamo di avere 100 elementi per ognuno dei 2 risultati. Bisogna impostare un controllo in
maniera tale che, quando abbiamo finito il nostro lavoro, dobbiamo uscire dal loop. Generalmente i
loop sono gestiti in maniera decrescente. O parto da R1 che va da 0 a 99, e confronto R1 alla
fine con 99 per vedere se sono arrivato alla fine E pi costoso dal punto di vista del tempo
piuttosto che confrontare R1 con 0. Per questo si fa in modo che R1 punti allultimo elemento del
vettore e, mano a mano, si decrementa e quando punta a 0 ho la condizione di uscita. Quando R1
punter a 0, devo far si di stare fuori dal mio vettore, da 1 a 100 dentro il loop, 0 fuori per poter
impostare cos un loop del tipo BNEZ R1. L'ultimo elemento del vettore, che il 1 di memoria,
sar qualcosa ottenuta con R1 > 0 dove R1 non passa di 1 in 1, ma se sono dati di 8 byte andr di
8 in 8. Il primo elemento sar caricato allindirizzo 1000, il 2 1008, il 3 1016 fino allindirizzo 1792.
Avremo la memoria occupata da 1000 a 1800 ( 100 elementi da 8 byte) dove lultimo elemento, da
8 byte, parte da 1792. Ciascuno skippa di 8 byte in memoria, quindi R1 verr incrementato ( nel
nostro caso decrementato )di 8 e quando arriver a 0, dovr puntare fuori dal vettore e uscire.
Come mi costruisco la struttura? Faccio si che loffset ( quello che ho scritto pari a
1000,2000,3000) al posto di 1000 vada 992, al posto di 2000 1992 e al posto di 3000 2992.
R1 partir da 800 e via via si decrementer in modo tale che 800 +992 = 1792 Ultimo elemento.
Poi incremento. Quando R1 vale 8 ho 992+8=1000, lo decremento, R1 vale 0, BNEZ non
soddisfatta e sono uscito dal loop. Lalgoritmo : R1 parte da 800, quindi posso fare un ADDI ( in
blu). Devo decrementare il mio R1 di 8 ( sommo limmediato -8), se R1 non 0 salto e conto 6
istruzioni ( il compilatore prender limmediato -6 aggiunger due 00 e avr -24). Questo il mio
loop. E scritto bene? Dal punto di vista della logica e del funzionamento scritto bene, ma dal
punto di vista delle prestazioni non il massimo, perch gi nella 2 LOAD ho uno stallo( prodotto

fa uso di F2), dopo la MULT ho 7 stalli. Facciamo la STORE, decrementiamo e facciamo la BNEZ
ma avr 1 stallo anche qua perch la BNEZ si basa su R1 e questo R1 non stato ancora
calcolato. Per ridurre gli stalli potrei spostare un po le istruzioni mettendo l ADDI dopo la LOAD,
eliminando lo stallo di prima e anche quello che stava dopo ADDI. Siccome i due R1( uno pi
piccolo dellaltro di 8, per questo quello che stato tolto a uno lo possiamo regalare allimmediato
delloperazione dopo) lavorano con un immediato, so con che cosa sto trattando e quindi nel caso
della STORE faccio 3008. Ci possibile perch ho delle quantit note come gli immediati, se ci
fosse stato un registro al posto dell'immediato non avrei potuto fare ci.
Da un loop che dura 6 istruzioni, in totale ne dura 10 Quasi raddoppio il tempo per eseguire
questo loop. Interveniamo ( compilatore) allora con ci che avevamo detto essere lo srotolamento
del loop. Supponiamo che in uniterazione del loop non lavoro soltanto su un elemento del vettore,
ma sul 1,2,3,4,5 poi faremo unaltra iterazione del loop e lavoreremo dal 6 al 10 elemento.
Ogni volta che prendiamo uniterazione, stiamo lavorando 5 elementi del vettore. Questo fa si che
le iterazioni saranno quel numero diviso 5. Attenzione, per, che ogni iterazione far un
determinato lavoro. Vediamo cosa succede se organizzo lo srotolamento in 4;
ADDI R1 800 R1
LOAD F1 992 R1
LOAD F2 1992 R2
MULT F3 F1 F2
STORE F3 2992 R1
ADDI //momentaneamente la tolgo, procedo con la carica del 2 elemento
Questo il gruppo che fa loperazione sul 1 elemento del vettore. Non faccio lincremento di 8,
altro loop e incremento di -8 perch se no farei 4 volte il decremento. Vado con la carica dellaltro
elemento e faccio man mano il decremento di 8. R1 rimasto non decrementato e allora posso
fare 992-8.

LOAD F1 984 R1
LOAD F2 1984 R1
MULT F3 F1 F2
STORE F3 2984 R1
Lo faccio altre 2 volte. Dopo aver srotolato 4 volte, decremento R1, per devo decrementare R1 e
la prossima volta devo puntare a 960+800 e, siccome devo fare 992 + 800, allora devo
decrementare R1 di 32. Dopo 968 ho ADDI R1 R1-32. Sostanzialmente questo decremento vale
quello che faccio in un loop, -8, moltiplicato il numero delle repliche che ho messo nel loop
srotolato. Ne ho messe 4, quindi 8x4= 32 e decrementer di 32. Il codice cos scritto corretto ma
non massimo. Qualche miglioramento c, perch avr una sola ADDI e una sola BNEZ.
Abbiamo srotolato per ridurre gli stalli tra la MULT e la STORE. Ci accorgiamo che continuano a
esistere questi stalli. Se prima avevamo 6 stalli, anche ora li ho. Ho 4 gruppi di 6 stalli; sembra che
non ho risolto molto. Attenzione: fare la STORE dopo la MULT non indispensabile, qualche
istruzione tra la MULT e la STORE si pu cercare e mettere, per esempio, la 2 MULT sotto se ho
messo le altre 2 LOAD sotto e via dicendo. Che cosa avviene se metto tutte le LOAD insieme, tutte
le MULT insieme? Il codice viene scritto nel seguente modo:
L X1

L Y1
L X2
L Y2
L X3
L Y3
L X4
L Y4
M Z1
M Z2
M Z3
M Z4
S Z1
S Z2
S Z3
S Z4
Se suppongo che la MULT duri 3 colpi di clock, vediamo che non ho problemi di stallo. Pago il fatto
che la MULT, che usa Z1, usa X1 e Y1. Se usassi gli stessi registri, sovrascriverei quelli della prima
coppia. (Se la MULT avesse latenza per 6 srotolerei per 6). Ho un loop con pi istruzioni e sono
riuscito a evitare del tutto gli stalli. Per ogni coppia devo usare una coppia differente Sto usando
pi registri. Quando faccio ci ho 2 costi: un costo che, in memoria programma, ho un codice
pi lungo avendo pi istruzioni, ma un costo abbastanza trascurabile. Quello pi critico , come
gi detto, l'utilizzo di pi registri Questo il vero costo che si paga.