Perché la maggior parte dei sistemi RISC implementa un'architettura load/store?
Due concetti entrano in gioco qui:
- Ortogonalità
- Minimalismo
Ortogonalità significa che si possono combinare operazioni insieme con restrizioni minime. Per esempio, supponiamo che il secchio A contenga "modalità di indirizzamento degli operandi" e il secchio B contenga "operazioni matematiche". Un'architettura completamente ortogonale permetterebbe di combinare qualsiasi modalità di indirizzamento degli operandi dal bucket A con qualsiasi operazione matematica dal bucket B.
Minimalismo, specialmente nel contesto di un'architettura RISC, significa scomporre le operazioni nei loro pezzi fondamentali in un modo che permette di combinarle come si vuole. L'obiettivo è quello di avere un numero minimo* di pezzi che possono essere combinati in tutte le operazioni che si possono immaginare. I pezzi dovrebbero essere abbastanza semplici da richiedere solo 1 ciclo di esecuzione nella pipeline. Se hai bisogno di sommare due numeri, se possibile vorresti limitarti ai modi fondamentali in cui puoi sommare due numeri, senza considerare da dove vengono gli operandi.
Nota: Se esamini MIPS, SPARC, ARM, e altri processori RISC / RISC-like, scoprirai che non c'è necessariamente un consenso su ciò che costituisce un insieme fondamentale di operazioni. Per esempio, avete bisogno di addizioni separate con segno e senza segno? Larghezze di parola diverse? Alla fine, se puoi farlo in un solo ciclo, puoi ottenere un lasciapassare...
In ogni caso, questo ha portato all'idea di separare gli accessi alla memoria dai calcoli. Permette di mantenere l'ortogonalità pur raggiungendo il minimalismo.
Nelle macchine CISC, troverete un mix di istruzioni memoria-registro, registro-memoria e registro-registro. Per esempio, in x86, posso fare un ADD tra un valore in memoria e un valore in un registro, scrivendo il risultato in memoria. Oppure posso sommare due valori in registri, scrivendo il risultato in un registro. Oppure posso sommare un registro con un valore in memoria, scrivendo il risultato in un registro. Se si vuole rendere una tale macchina ortogonale, si finisce per avere istruzioni per ogni combinazione di modalità di indirizzamento incrociata con ogni tipo di calcolo supportato.
In un'architettura RISC-like Load/Store, l'accesso alla memoria è fattorizzato nelle proprie istruzioni. Così, invece di aver bisogno di [math]O(\mbox{modes} \times \mbox{operations})[/math] istruzioni per raggiungere la piena ortogonalità, avete solo bisogno di [math]O(\mbox{modes} + \mbox{operations})[/math] istruzioni.
La vostra pressione delle istruzioni scende da quadratica a lineare. Questo rende molto più facile raggiungere l'ortogonalità reale.
Load/store ha un ulteriore vantaggio: gli accessi alla memoria sono costosi. Spesso hanno latenze grandi e imprevedibili. Fattorizzare gli accessi alla memoria fuori dal calcolo regolare rende più facile programmare gli accessi alla memoria indipendentemente dalle istruzioni che dipendono da essi. Questa è una grande parte del motivo per cui le architetture CISC trasformano le istruzioni CISC in µops simili a quelli del RISC: rende più facile gestire gli effetti del sistema di memoria.
Le prime architetture RISC non sfruttavano questo aspetto come fanno le macchine moderne. Le prime architetture RISC pensavano che esporre la latenza di una lettura della memoria fosse una buona idea, e così hanno introdotto nel mondo l'idea di uno slot per il ritardo di caricamento. Sfortunatamente, questo concetto non è scalabile quando è necessario cambiare la pipeline. Se state facendo processori embedded per i quali siete felici di programmare staticamente le istruzioni, potete cavarvela. (L'ho fatto per circa 20 anni.) Non funziona davvero per i processori mainstream che devono eseguire binari per il codice che non si può ricompilare.
Le moderne architetture RISC/RISC-like funzionano semplicemente con il vantaggio intrinseco che l'accesso alla memoria è separato dalla computazione, e usano varie tecniche di scoreboarding per programmare dinamicamente le istruzioni nella pipeline quando i loro argomenti sono disponibili.
Un ultimo pensiero: In questi giorni, considero RISC e CISC più etichette di marketing che qualsiasi tipo di classificazione rigorosa. Ci sono molte macchine che sostengono di essere RISC che non assomigliano per niente al minimalismo spoglio del MIPS R2000. Dall'altro lato, le moderne macchine CISC ottimizzano il loro sottoinsieme più simile al RISC, rendendo facile la scomposizione delle istruzioni in pezzi simili al RISC da eseguire sulla microarchitettura sottostante.
* OK, non assolutamente minimale. C'è una soglia pratica. Potresti ridurre tutto a NAND al limite, ma non lo farai se stai costruendo un processore pratico. Ad un certo punto, tagliare le fasi della pipeline ancora più strette fa sì che le operazioni comuni, seriali e di percorso critico richiedano più cicli di clock e più tempo di wall-clock.
In pratica, sembra che un'addizione intera alla dimensione della parola macchina sia la soglia, almeno sui processori su cui ho lavorato. Se sei a questo livello o al di sotto, sei a posto. Se sei al di sopra, vieni tagliato in più cicli o più operazioni. Perché? Perché l'accumulazione (foo += bar) è estremamente comune, e fare in modo che questa operazione richieda più cicli farebbe esplodere il numero di cicli di troppe cose.
Questo significa che le macchine pratiche a marchio RISC non sono così "ridotte" come implica l'acronimo. Come ho detto, è più un'etichetta di marketing che una stretta classificazione ingegneristica.
Articoli simili
- Qual è la differenza tra l'architettura ARM e il RISC regolare?
- Perché la maggior parte degli smartphone Samsung si blocca e si blocca? Perché la maggior parte degli smartphone indiani si blocca?
- Qual è la soluzione per l'errore del messaggio 'Couldn't load model schema' quando si lancia Power Bi?
- Implementate prima il front-end o il back-end? Quando si implementa l'UI?