CAP. 1 PROCESSORI SUPERSCALARI
La storia del mercato dei microprocessori a partire dall’inizio degli anni ’90
potrebbe riassumersi come un sostanziale miscuglio delle caratteristiche della
filosofia di progettazione RISC (Reduced Instruction Set Computing) e delle
caratteristiche della filosofia di progettazione CISC (Complex Instruction Set
Computing) con maggior attenzione al raggiungimento delle migliori prestazioni più
che ai "precetti" che questi due acronimi sottendono. Naturalmente le differenze
strutturali tra i due approcci permangono (una su tutte: il numero di registri
indirizzabili dai rispettivi set di istruzioni che consente ad esempio una gestione del
compilatore per l’ottimizzazione più aggressiva da parte dei processori RISC) ma si
vogliono mettere in luce , in questo capitolo , le principali soluzioni adottate dai
processori che stanno dominando il mercato dei PC. Durante gli ultimi anni, spinte
dalle evoluzioni del mercato sia nella crescente domanda di migliori prestazioni,
soprattutto in campo grafico e multimediale, che dall’evoluzione di Internet, le
principali aziende produttrici si date battaglia, più che nel realizzare sistemi
veramente innovativi, nel portare all’esasperazione la ricerca della velocità di
esecuzione maggiore per mezzo delle tecniche usate negli ultimi 10 anni
raggiungendo e superando, anche grazie a nuovi processi tecnologici, con i processori
AMD Athlon e Intel Pentium 4 la soglia del GHz di clock. A parte l’esecuzione più
veloce delle singole operazioni, che attiene prettamente alla tecnologia impiegata
nella realizzazione degli elementi logici e alla loro organizzazione, si possono
eseguire un numero maggiore di operazioni in parallelo per incrementare le
prestazioni ossia si persegue lo sviluppo a livello di parallelismo delle istruzioni
(ILP). Le strade maestre per questo obiettivo sono essenzialmente la realizzazione di
pipeline più profonde (cioè con un numero di stadi maggiore) e la superscalarità che
devono essere però accompagnate da tecniche di controllo ,predizione dei salti e
riordino delle istruzioni. L’esecuzione in pipeline è naturalmente favorita da
istruzioni di ridotta complessità e questo è uno dei motivi per cui gran parte delle
risorse dei moderni processori x86 sono spese in fase di decodifica per scomporre le
complesse (e di lunghezza variabile) istruzioni del set ISA x86 in micro-istruzioni da
inviare alle diverse unità di esecuzione. Analogamente tecniche come la predizione
delle diramazioni o l’esecuzione fuori ordine (out-of-order) contribuiscono a
2
spostare la complessità dal software all’hardware, aspetto che sarebbe in
contraddizione con la filosofia RISC, ma vengono adottate da moltissimi processori
di ispirazione RISC, G4 compreso, perché consentono alte prestazioni. Molti
processori, implementando l’esecuzione fuori ordine, richiedono anche tecniche di
gestione delle false dipendenze dei dati come la rinomina dei registri (register
renaming) che consiste in una mappatura dinamica dei registri architetturali su un set
più esteso di registri fisici. Questo avviene anche per un processore RISC come il G4
che possiede un numero maggiore di registri indirizzabili rispetto a un processore di
architettura x86 e dovrebbe avvertire meno questa esigenza. I processori più recenti
hanno adottato soluzioni tutto sommato convenzionali in questi campi e le poche
novità derivano dalla spinta del progresso tecnologico che ha consentito soluzioni
quantitativamente diverse soprattutto per gestire l’aumentata profondità delle pipeline
(ad esempio si pensi alla pipeline del Pentium 4 di ben 20 stadi). La latenza di
memoria continua ad essere una delle grandi sfide dei moderni processori e anche in
questo campo le novità riguardano essenzialmente le possibilità offerte dalla
integrazione. Prestando particolare attenzione come detto alle applicazioni
multimediali che fanno grande uso della computazione intensiva per floating point si
è andato affermando negli ultimi due anni la tendenza ad estendere il parallelismo ai
dati di questa natura per processare un numero maggiore di elementi in minore tempo.
Questa tecnica già adottata con successo dalla tecnologia MMX introdotta da Intel
(che riguardava però dati interi) è conosciuta come Single Instruction Multiple Data
(SIMD) e si esplicita per mezzo della estensione del set di istruzioni dei processori
con apposite istruzioni. Per quanto riguarda questo ultimo aspetto abbiamo
implicazioni anche per quanto riguarda l’evoluzione tecnologica dato che ha
consentito l’integrazione di apposite unità esecutive dedicate a queste operazioni e
ricadute sulla gestione della latenza di memoria dato che questi set di istruzioni
prevedono generalmente istruzioni di gestione delle cache. Nel corso dell’ultimo anno
i maggiori produttori hanno messo a punto processi tecnologici a 0.18 micron a 6
livelli di metallizzazione con connessioni in rame o alluminio che hanno avuto come
effetti principali:
• Riduzione delle dimensioni dei chip a parità di densità di transistori e quindi
dei costi di produzione oppure incremento del numero dei transistori a parità
3
di dimensioni del chip e quindi più unità funzionali oppure una combinazione
delle due opzioni;
• Riduzione delle tensioni di alimentazione dei core e quindi della potenza
assorbita;
• Incremento delle frequenze di lavoro dei processori.
Mentre gli ultimi due effetti hanno immediate e ovvie ricadute sulle
prestazioni complessive di un processore, la possibilità di integrare un numero
maggiore di transistor può essere variamente sfruttata. Innanzitutto la possibilità di
integrare più unità funzionali consente una maggiore superscalarità e la
specializzazione di unità per specifiche applicazioni. Inoltre esiste la possibilità di
dotare di un numero maggiore di buffer le unità dedicate al riordino o al temporaneo
stoccaggio delle istruzioni per l’esecuzione fuori ordine o di incrementare le
dimensioni delle Branch History Table per consentire accurate predizioni delle
diramazioni che supportino adeguatamente le pipeline sempre più profonde.
L’aspetto di sicuro maggiore impatto sulla latenza di memoria è però la
possibilità sia di incrementare le dimensioni della cache di primo livello (si è giunti
ad avere 256 KB di cache L1) che di integrare la cache di secondo livello
direttamente su chip. Questa soluzione comporta una maggiore banda a disposizione e
una riduzione dei costi complessivi ed è la caratteristica saliente delle versioni dei
processori di questi ultimi tempi. Di questi aspetti si avvantaggiano soprattutto le
architetture x86 dato che necessitano mediamente di un 40% in più di cache istruzioni
o magari di precoded cache per la decodificazione delle istruzioni x86 e la sempre
maggiore quantità di cache a disposizione ha contribuito notevolmente a colmare il
gap di cui godevano nella computazione su interi rispetto i processori RISC.
Di fatto le cache L2 on chip occupano ormai gran parte del die, anche fino al
90% in alcuni processori. La realizzazione on chip della L2 consente inoltre di
aumentarne l’associatività per ridurre i conflitti raggiungendo con la Advanced
Transfer Cache della Intel il limite dell’associatività a 8 vie che, ai fini pratici,
equivale quasi alla piena associatività e altre caratteristiche, come ad esempio le porte
multiple, difficilmente ottenibili da una cache L2 esterna.
Per quanto riguarda la memoria principale si è avuta negli ultimi anni l’uscita,
sebbene travagliata sia da problemi tecnici che commerciali, della Rambus Direct
4
RAM (RDRAM) che ha consentito una banda di 1,6 GB/s con bus da 100 MHz sulla
quale Intel ha puntato molto e l’affinamento di tecniche come la Double Data Rate
SDRAM che hanno consentito anch’esse bande superiori al GB/s.
Da registrare infine la comparsa del bus di nuova generazione EV6 che,
implementando la topologia "Point to Point", ha consentito di collegare più
processori senza suddividere la banda aprendo la strada ad una tendenza che potrebbe
vedersi consolidata nei prossimi anni per un ulteriore salto nelle prestazioni. Altre
innovazioni tecniche, non strettamente legate al processo realizzativo, che si
annunciano assai significative sono state adottate da Intel con il Willamette come la
ALU funzionante a frequenza doppia rispetto al processore o il bus a 400 MHz.
Anche se i particolari tecnici di queste innovazioni non sono ancora perfettamente
noti essi sembrano riferirsi ad uno sfruttamento di entrambi i fronti di salita dei
segnali di clock.
1.1 Il parallelismo dei dati
Gli algoritmi utilizzati nelle applicazioni multimedia e DSP (Digital Signal
Processing) prevedono sostanzialmente l’applicazione della medesima operazione ad
un grande numero di dati del medesimo tipo. Un tipico esempio può essere
l’inversione di una immagine RGB per determinarne il negativo oppure la
riproduzione del movimento in una scena 3D. Il modo migliore di trattare dati di tale
natura consiste nel raggrupparne un certo numero all’interno di un vettore e operare
su di esso piuttosto che operare sui singoli dati (un dato alla volta) per analogia
definiti scalari.
La tecnica che sottende a questo procedimento è conosciuta come SIMD ed è
diventata una delle parole chiave dei processori di recente realizzazione che la
applicano a dati floating point che sono i più coinvolti nelle applicazioni
multimediali. A questa scelta, ancora una volta, sono giunti sia processori nativi CISC
che RISC sebbene l’introduzione di nuove istruzioni e di nuove unità a livello
hardware non appartenga certo al canone RISC. Le scelte che differenziano
l’implementazione di tale tecnica riguardano essenzialmente l’introduzione di
appositi registri o, analogamente a quanto fatto a suo tempo da Intel con la tecnologia
MMX su interi, la condivisione di registri esistenti.
5
Altre differenze riguardano il numero e le modalità di operazione delle unità
dedicate alla esecuzione delle istruzioni SIMD. Vantaggi derivanti dalle tecniche
SIMD sono ad esempio quelli del supporto dell’aritmetica di saturazione: se si
utilizza la classica aritmetica di arrotondamento quando, ad esempio in un
procedimento di incremento di colore, si giunge al limite di rappresentazione di un
dato un ulteriore incremento determina un overflow con il numero che raggiunge
erroneamente il limite opposto. Quando si sommano invece ad esempio due valori a
32 bit che raggiungono un valore che eccede i limiti di rappresentazione si avrebbe
semplicemente bisogno di indicare che si è raggiunto il valore massimo
rappresentabile. Questo è un evento particolarmente frequente in applicazioni
multimediali (ad esempio nei valori di colore dei pixel) e l’aritmetica di saturazione
serve appunto a questo.
Altri vantaggi derivano dalla possibilità di riempire i vettori che costituiscono
i data type delle unità SIMD con dati di varia dimensione (a patto di non eccedere
naturalmente la dimensione del data type) in modo da poter gestire il parallelismo per
più tipi di dati (e quindi diversi tipi di applicazioni).
Con l’occasione offerta dalla introduzione di nuove istruzioni tutte le maggiori
CPU hanno introdotto nel contempo istruzioni dedicate alla gestione via software di
tecniche di prefetching cioè di caricamento dei dati prima del loro utilizzo per
minimizzare la latenza dell’accesso ai dati mediante la scelta circa le modalità di
scrittura in memoria e il livello di cache interessati. Inoltre sono state predisposte
istruzioni dette di streaming store per gestire dati caratterizzati da scarsa località
temporale (ad esempio utilizzati una sola volta) tipici di applicazioni video e 3D che
causerebbero un inutile “inquinamento” della cache e che sono gestibili meglio
secondo politiche di cache di tipo write-combining. Tale tecnica prevede
l’accumulazione in un buffer apposito di più write e il loro trasferimento in un solo
accesso alla memoria principale riducendo così il traffico rispetto alla modalità write-
allocate che alloca una nuova linea nella cache su un write miss.
Concettualmente ogni processore può essere suddiviso in due blocchi
principali. Il primo si occupa di prelevare le istruzioni dalla gerarchia di memoria e di
predisporle alla esecuzione. Questo significa che le istruzioni devono essere
decodificate (mediante suddivisione in microistruzioni o decodifica hardware) per
renderle "comprensibili" dalle unità di esecuzione e quindi distribuite ognuna alla
propria unità di esecuzione. Questo blocco è conosciuto come il Front End del
6
processore. Naturalmente quante più istruzioni esso riesce a inviare alle unità di
esecuzione tanto più è efficiente. Importante per questo stadio è la comunicazione con
la gerarchia di memoria (quindi le modalità e la velocità con cui essa opera) per
rifornire continuamente di istruzioni il Front End, e la predizione delle diramazioni
(salti) che cerca di impedire che la pipeline del processore abbia stadi che non
eseguono alcuna operazione in attesa che sia noto l’esito della condizione di
diramazione.
Il secondo stadio del processore è quello che esegue effettivamente il "lavoro"
cioè esegue le istruzioni e fornisce i risultati. Questo stadio è chiamato Back End.
L’efficienza di questo stadio è fortemente condizionata dal numero di unità di
esecuzione di cui dispone (per cominciare l’esecuzione di più di una istruzione per
ciclo) e dal tempo di cui necessita ciascuna unità per processare una istruzione. I
processori moderni fanno uso dell’esecuzione fuori ordine proprio per fare fronte alla
carenza di risorse (registri, unità di esecuzione) di cui generalmente soffrono.
Quello che qui si intende sottolineare è che questi due stadi influenzano
reciprocamente le proprie prestazioni. Un Back End che esegue un elevato numero di
istruzioni per clock deve essere continuamente rifornito di nuove istruzioni dal Front
End da elaborare per non essere sottoutilizzato. Analogamente, per facilitare il lavoro
di distribuzione delle istruzioni alle unità di esecuzione del Front End, le istruzioni
devono essere eseguite efficacemente per liberare le risorse che esse impegnano. Non
è un caso se un processore come l’Athlon, che è dotato ad oggi probabilmente del più
efficace Back End del mercato x86, sia stato fornito anche del Front End con il
maggior numero, effettivo, di istruzioni decodificate per clock. Analogamente
l’Itanium che, grazie alla definizione di una nuova architettura (EPIC), riesce a
decodificare e inviare alle unità di esecuzione ben 6 istruzioni per ciclo è stato dotato
di massicce risorse di esecuzione. Un processore che non rispettasse questo principio
di bilanciamento sarebbe destinato all’insuccesso.
1.2 Limiti delle architetture a 32 bit
L’architettura x86 o IA-32 (Intel Architecture-32 bit) è vecchia di oltre 20
anni e sta mostrando, ora che le sue potenzialità sono state spinte all’estremo dalle
recenti architetture, i propri limiti. Essi sono essenzialmente i seguenti:
7
• Lunghezza variabile delle istruzioni: ciò comporta una fase di
decodifica molto complessa che per compensazione richiede frequenze
di lavoro più alte o pipeline più profonde con relativi problemi di
predizione delle diramazioni.
• Carenza dei registri: gli indirizzi dei registri nell’architettura x86 sono
di soli 3 bit che consentono quindi l’indirizzamento di soli 8 registri di
uso generale. I moderni processori hanno cercato di superare questo
limite per mezzo di tecniche come la ridenominazione dei registri che
aggiungono però ulteriore complessità.
• Floating Point Stack: le istruzioni x87 usano per le proprie operazioni
uno stack del quale viene fatto un uso intensivo con spreco di molti
cicli per posizionare i dati occorrenti alla sua cima e che costituisce la
ragione principale della sostanziale inferiorità delle CPU x86 rispetto
le CPU RISC per la computazione floating point.
• Istruzioni che referenziano indirizzi di memoria: con evidenti
rallentamenti dovuti alle latenze associate ai chip di memoria in
confronto a quelle che caratterizzano i registri interni della CPU
(mentre le CPU RISC adottano un modello di memoria Load/Store che
prevede le istruzioni operino solo tra registri e gli accessi alla memoria
siano espliciti e deputati solo alle istruzioni di load e store).
• Dimensioni del die: i buffer sempre più grandi, sempre più sviluppate
tecniche di branch prediction (Intel stima che le penalità di errata
predizione delle diramazioni comportino diminuzioni del 20%-30%
delle prestazioni dei processori), la decodifica di istruzioni x86 in
microistruzioni RISC sono tutti accorgimenti per estrarre grandi
prestazioni dalle CPU x86 ma che richiedono molti transistor e quindi
molto spazio sui die e quindi aumenti dei costi.
La Intel ha ritenuto che, per assicurare l’incremento delle prestazioni che ha
visto la recente storia del mercato dei PC, fosse opportuno cambiare radicalmente il
set di istruzioni anche a costo di penalizzare, naturalmente il meno possibile, la
compatibilità con l’architettura precedente ma offrendo nel contempo prestazioni con
le nuove architetture tali da giustificare un simile passo. La strada scelta da Intel per
8
aumentare le prestazioni è quella di incrementare il parallelismo a livello di istruzioni
(ILP).
Figura 1
Le due strade privilegiate sino ad oggi per conseguire questo obiettivo sono
mostrate in fig. 1 : la superscalarità nella quale il parallelismo non è esplicitamente
indicato ma viene estratto mediante scheduling dinamico delle istruzioni in pratica
deputando all’hardware tutto il lavoro (figura a sinistra) e la tecnica VLIW (Very
Long Instruction Word) secondo la quale le istruzioni contengono esplicite
informazioni riguardanti il parallelismo da esplicitare, ad esempio quante e quali
istruzioni elaborare parallelamente che il compilatore raggruppa in pacchetti di
dimensione fissata (in genere tra 112 e 168 bits inserendo NOP per riempire gli slots
vuoti del pacchetto) e invia alle unità esecutive (figura a destra). In questo caso il
grosso del lavoro è svolto dal compilatore e l’esecuzione è fissata staticamente al
momento della compilazione.
L’approccio usato da Intel e HP (per esempio nell’ITANIUM) è una
combinazione delle due strade per sfruttarne le caratteristiche di dinamicità e staticità
9
di ognuna ed è stato chiamato Explicitly Parallel Instruction Computing (EPIC).
La filosofia EPIC in sostanza lascia decidere al compilatore quali istruzioni debbano
essere eseguite in parallelo, le riunisce in un bundle (fascio) e manda in esecuzione
questi bundle. In aggiunta a questo la tecnica EPIC prevede tipiche azioni di controllo
e correzione della esecuzione come Predication oppure Speculation (per dati e
istruzioni) che consente il loro caricamento prima della risoluzione delle relative
dipendenze per ridurre gli effetti della latenza di memoria.
Le caratteristiche principali di questo approccio sono:
• la possibilità di far collaborare il compilatore e l’hardware in modo che
il compilatore possa controllare le risorse hardware in maniera più
diretta anche a livello di predizione delle diramazioni o della gestione
della gerarchia di memoria. Un tipico esempio di questa collaborazione è
la tecnica di Predication che consente ad esempio l’esecuzione in caso di
una diramazione IF THEN ELSE di entrambi i THEN e ELSE (che non
dipendono uno dall’altro) in parallelo (e quindi con la latenza di uno
solo) salvo poi invalidare quello non corretto mediante apposito valore
dei flag di Predicate assegnati a ciascuna delle diramazioni all’atto della
loro esecuzione;
• il grande numero di registri utilizzabili che, oltre ad una computazione
sia di interi che floating point più agevole specie a doppia precisione,
fornisce adeguate risorse hardware per funzioni specializzate come la
Predication e consente grande flessibilità ed estensibilità circa la
superscalarità dei chip basati sul ISA IA-64 (è difficile per un processore
x86 gestire un numero elevato di pipeline con solo 8 registri
indirizzabili). Inoltre è possibile ridurre gli accessi alla memoria solo
alle operazioni load e store sfruttando i registri per i risultati intermedi;
• grande memoria indirizzabile grazie ai registri a 64 bit che consente di
superare gli attuali 4 GB dell’architettura IA-32, i cui limiti peraltro non
sono ancora stati raggiunti dalle applicazioni attuali ma potrebbero
esserlo in futuro, e di supportare numerosi GB di RAM.
Tutte queste caratteristiche sono facilmente catalogabili come tipiche
caratteristiche RISC.
10
Il problema della filosofia EPIC risiede esattamente nel coinvolgimento del
compilatore e nella definizione di un nuovo ISA per cui la esecuzione di codice IA-32
risulterebbe problematica ed inoltre potrebbe comportare, senza un adeguato supporto
peraltro già perseguito da Intel, un certo ritardo nello sviluppo di software che possa
avvantaggiarsi di questa architettura. Inoltre l’esecuzione in ordine e il
raggruppamento statico delle istruzioni tipiche delle CPU EPIC possono poco contro i
miss della cache che sono poco prevedibili a livello di compilazione e per alcune
applicazioni come Java non è detto che il tempo speso in fase di compilazione possa
essere adeguatamente compensato dai tempi di esecuzione.
Un’altra strada a disposizione degli sviluppatori per incrementare le
prestazioni sfruttando il parallelismo consiste nel Thread Level Parallelism (TLP)
cioè nella esecuzione in parallelo da parte di sistemi multiprocessore, che possono
essere fisici (cioè con la presenza effettiva di più CPU sul medesimo chip come nel
POWER4 di IBM) o virtuali (cioè ottenuti da un singolo chip che emula un sistema
multiprocessore simmetrico come l’EV8 della Compaq), di differenti processi
software (o thread). Questo approccio trova le sue ragioni e naturali applicazioni
nell’ambito del mercato server dove effettivamente molte applicazioni possono essere
effettivamente scomposte in più threads e assegnati ai diversi processori che
costituiscono il sistema. In ambito desktop attualmente l’implementazione del TLP ha
un impatto assai contenuto ma le applicazioni multimediali sono naturalmente
orientate al TLP e quando nuovi sistemi operativi, come Windows 2000 che supporta
sistemi multiprocessore, o linguaggi di programmazione come Java, che predispone a
sviluppare applicazioni multithread, diventeranno standard le cose potrebbero
sensibilmente cambiare.
L’approccio TLP in sostanza pone la sua attenzione sul design di sistema più
che non sulla definizione di un nuovo ISA e in particolare, dovendo prevedere la
condivisione di risorse tra i diversi processori, sulla banda di memoria e difatti la
IBM con il POWER4 ha speso molte risorse in questa direzione. I sistemi TLP inoltre
potrebbero essere più liberi nella evoluzione hardware rispetto alle macchine EPIC e
limitati più che altro dalla tecnologia (il POWER4 ad esempio consiste di 170 milioni
di transistor su 400 mm
2
di die). Grande attenzione inoltre deve essere posta per
questi processori nella condivisione delle risorse, soprattutto per quelle del sistema di
memoria, per non creare interferenze di cache tra i diversi threads.
11
AMD, proprio per motivi di compatibilità con il set di istruzioni x86 e per il
fatto che non ha probabilmente la forza di definire un nuovo ISA, ha optato con il K8
(o Sledgehammer) per la strada dell’estensione del set istruzioni x86 sul modello di
quanto fatto da Intel nel passaggio da 16 bit a 32 bit e per l’adozione del sistema di
più processori sul medesimo die che è un approccio più performante rispetto ad
esempio ai sistemi simmetrici multiprocessore, la naturale evoluzione dell’impiego
dell’architettura IA-64 soprattutto in ambito server, dato che non devono comunicare
attraverso un bus lento e occupano meno spazio.
Naturalmente queste unità esecutive poste sul medesimo die avranno anche
importanti potenziamenti per superare alcuni dei limiti sopra esposti. Ad esempio per
la computazione su floating point si farà uso di un nuovo modello di programmazione
e di istruzioni per la FPU di tipo RISC (con l’uso di tre operandi) che si serviranno di
un largo register file per floating point in luogo del vecchio e limitante stack x87.