Ci sono molte ragioni per cui non hai solo un numero enorme di registri:
- Sono altamente collegati alla maggior parte delle fasi della pipeline. Per cominciare, è necessario monitorare la loro durata e riportare i risultati alle fasi precedenti. La complessità diventa intrattabile molto rapidamente e il numero di fili (letteralmente) coinvolti cresce alla stessa velocità. È costoso in area, il che alla fine significa che è costoso in termini di potenza, prezzo e prestazioni dopo un certo punto.
- Occupa spazio per la codifica delle istruzioni. 16 registri occupano 4 bit per sorgente e destinazione e altri 4 se si hanno istruzioni a 3 operandi (es. ARM). È una quantità enorme di spazio di codifica del set di istruzioni occupato solo per specificare il registro. Ciò alla fine influisce sulla decodifica, sulla dimensione del codice e ancora sulla complessità.
- Esistono modi migliori per ottenere lo stesso risultato ...
Oggigiorno abbiamo davvero molti registri, semplicemente non sono programmati esplicitamente. Abbiamo "rinominare i registri". Anche se si accede solo a un set piccolo (8-32 registri), in realtà sono supportati da un set molto più grande (ad esempio 64-256). La CPU quindi tiene traccia della visibilità di ogni registro e li assegna al set rinominato. Ad esempio, è possibile caricare, modificare, quindi memorizzare in un registro molte volte di seguito e fare in modo che ciascuna di queste operazioni venga effettivamente eseguita in modo indipendente a seconda dei mancati riscontri nella cache, ecc. In ARM:
ldr r0, [r4]
add r0, r0, #1
str r0, [r4]
ldr r0, [r5]
add r0, r0, #1
str r0, [r5]
I core Cortex A9 registrano la ridenominazione, quindi il primo caricamento su "r0" va effettivamente a un registro virtuale rinominato - chiamiamolo "v0". Il caricamento, l'incremento e l'archiviazione avvengono su "v0". Nel frattempo, eseguiamo di nuovo un caricamento / modifica / memorizzazione in r0, ma verrà rinominato in "v1" perché questa è una sequenza completamente indipendente che utilizza r0. Diciamo che il carico dal puntatore in "r4" si è bloccato a causa di un errore nella cache. Va bene - non è necessario attendere che "r0" sia pronto. Poiché è stato rinominato, possiamo eseguire la sequenza successiva con "v1" (anch'esso mappato su r0) - e forse è un successo nella cache e abbiamo appena ottenuto un enorme successo in termini di prestazioni.
ldr v0, [v2]
add v0, v0, #1
str v0, [v2]
ldr v1, [v3]
add v1, v1, #1
str v1, [v3]
Penso che x86 sia fino a un numero enorme di registri rinominati in questi giorni (ballpark 256). Ciò significherebbe avere 8 bit per 2 per ogni istruzione solo per dire qual è l'origine e la destinazione. Aumenterebbe enormemente il numero di fili necessari attraverso il nucleo e le sue dimensioni. Quindi c'è un punto debole intorno ai registri 16-32 che la maggior parte dei progettisti ha scelto, e per i progetti di CPU fuori ordine, la ridenominazione dei registri è il modo per mitigarlo.
Modifica : l'importanza dell'esecuzione fuori ordine e la ridenominazione del registro su questo. Una volta che hai OOO, il numero di registri non ha molta importanza, perché sono solo "tag temporanei" e vengono rinominati in un insieme di registri virtuali molto più grande. Non vuoi che il numero sia troppo piccolo, perché diventa difficile scrivere piccole sequenze di codice. Questo è un problema per x86-32, perché gli 8 registri limitati significano che molti temporanei finiscono per passare attraverso lo stack e il core ha bisogno di logica aggiuntiva per inoltrare letture / scritture in memoria. Se non hai OOO, di solito parli di un core piccolo, nel qual caso un set di registri di grandi dimensioni è uno scarso vantaggio in termini di costi / prestazioni.
Quindi c'è un punto debole naturale per la dimensione del banco di registro che arriva al massimo a circa 32 registri progettati per la maggior parte delle classi di CPU. x86-32 ha 8 registri ed è decisamente troppo piccolo. ARM ha 16 registri ed è un buon compromesso. 32 registri sono un po 'troppi se non altro: finisci per non aver bisogno degli ultimi 10 o giù di lì.
Niente di tutto questo tocca i registri extra che ottieni per SSE e altri coprocessori a virgola mobile vettoriale. Quelli hanno senso come set aggiuntivo perché funzionano indipendentemente dal core intero e non aumentano la complessità della CPU in modo esponenziale.