A metà degli anni ’90 nel mondo delle CPU fecero il loro ingresso le cosiddette estensioni SIMD (Single Instruction, Multiple Data), prima relegate a supercomputer, che cambiarono le carte in tavola della computazione.
Questo perché si passò dalla necessità di dover eseguire più velocemente un determinato tipo di calcoli, a quella di parallelizzarli, considerato che ben si prestavano allo scopo a causa dell’applicazione delle medesime istruzioni su dati diversi (in genere contigui).
Con la versione 6 della sua architettura ARM decise di seguire le orme degli altri produttori di microprocessori, ma ovviamente lo fece a modo suo…
In un precedente articolo abbiamo visto come abbia esteso l’ISA aggiungendo, nella versione 5, il necessario per supportare delle funzionalità tipiche dei DSP .
Con la versione 6 consolida questa tendenza, aggiungendo un’istruzione di moltiplicazione & accumulazione senza segno 32×32 + 64 -> 64 bit, introducendo tre istruzioni di moltiplicazione 16×16 bit con accumulazione (a 32 o 64 bit) o senza, un altro paio che eseguono due moltiplicazioni 16×16 (sempre con accumulazione), e altre quattro 32×32 (anch’esse con accumulazione) che fanno uso soltanto dei 32 bit alti generati dall’operazione.
Ma il pezzo forte è ovviamente rappresentato dalle nuove istruzioni che operano parallelamente su più dati, e che permettono di eseguire somme o sottrazioni su coppie di interi a 16 bit, oppure su 4 interi a 8 bit alla volta. Sono state aggiunte anche un paio di istruzioni di somma e sottrazione abbastanza strane (almeno per me, che non lavoro con DSP e affini) che, penso, serviranno per particolari algoritmi (ma sufficientemente comuni da giustificarne l’implementazione).
Queste 6 istruzioni sono disponibili, a loro volta, in diverse varianti. Possono essere con o senza segno, con o senza saturazione del risultato (limitatamente al range di valori su cui operano), e infine possono lavorare con o senza segno (ma non con saturazione) dimezzando poi il risultato (per evitare overflow).
Lavorare frequentemente con dati a 8 o 16 bit porta spesso alla necessità di doverli estrarre, per cui ARM ha pensato bene di arricchire il set d’istruzioni con alcune che consentono di estrapolare (o “spacchettare” in gergo) queste quantità, estendendole con segno o con zero, e opzionalmente sommando il risultato a un altro registro.
E’ anche possibile lavorare con due valori a 8 bit da estendere a 16 bit, prelevandoli rispettivamente dai bit 0-7 e 16-23. Per tutte le operazioni di estrazione è possibile specificare una rotazione di 0, 8, 16 o 24 bit, in modo da posizionare opportunamente i dati sorgente prima dell’estrazione.
A completare l’opera sono state aggiunte varie altre istruzioni. Di particolare rilevanza sono quelle per eseguire shift (a destra o a sinistra) combinando due valori a 32 bit come se fosse un unico valore a 64 bit, scambiare l’ordine dei byte di una valore a 32 bit, eseguire la saturazione con o senza segno di valori a 8 o 16 bit, impostare l’endianess per il load & store dei dati, e infine selezionare i byte da due sorgenti a seconda degli appositi flag (GE).
I flag GE (Greather than or Equal) sono 4 e sono stati aggiunti in questa ISA perché consentono di tenere traccia di eventuali overflow (cambiamenti di segno per le operazioni su dati con segno, oppure riporti per quelle senza segno). Ognuno viene settato in base al risultato degli 8 bit a cui è assegnato (per operazioni a 16 bit i rispettivi GE assumono lo stesso valore).
Sono parecchie, quindi, le innovazioni introdotte dalla versione 6 di quest’architettura, ma non diranno molto a chi conosce estensioni SIMD di qualche altra architettura. Ordinaria amministrazione, insomma. Nell’articolo parlo, però, di “approccio” di ARM, e in seguito specifico che lo fece “a modo suo”.
Il motivo deve andare a ricercarsi nella “solita” filosofia a cui ci ha abituati ARM: piccole modifiche al core per risparmiare transistor, senza stravolgere l’architettura, e consentendo quindi di mantenere bassi i costi, il consumo, e la dissipazione di calore.
In effetti confrontando la sua soluzione con quelle implementate in altre architetture, la cosa che balza subito all’occhio è che, come aveva già fatto con le estensioni DSP, tutte queste istruzioni agiscono sui normalissimi registri della CPU.
Al contrario, le estensioni SIMD di Sun (VIS) per i suoi UltraSparc (che introdussero per la prima volta il concetto di SIMD nelle CPU più commerciali), le MMX di Intel e le 3DNow! di AMD fanno uso degli stessi registri dell’FPU, mentre la famiglia SSE ha introdotto appositi registri.
Il risultato è che l’uso di queste istruzioni per gli ARM è del tutto trasparente e “a costo zero”, mentre VIS, MMX e 3DNow! devono far uso di particolari istruzioni quando si vuole passare dalla modalità SIMD alla tradizionale FPU (e viceversa), imponendo un certo ritardo prima dell’utilizzo. Peggio ancora le SSE, che introducendo nuovi registri richiedono spazio aggiuntivo per la loro memorizzazione, con conseguente maggior aumento dei tempi di context switch.
Ovviamente anche le rose hanno le spine, e il modello adottato da ARM rimane penalizzante sotto il profilo prestazionale per tre motivi. Il primo è che usando gli stessi registri del core per funzionalità SIMD, si riduce il numero di quelli disponibili per operazioni generiche.
Il secondo, molto più pesante, è legato alla dimensione dei registri, che con soli 32 bit consente di eseguire soltanto 2 calcoli a 16 bit o 4 a 8 bit in parallelo, mentre ovviamente con registri a 64, 128 o 256 bit (e si parla già di 512 e più bit per i successori delle SSE) il numero di operazioni eseguibili in parallelo risulta di gran lunga superiore.
Il terzo, infine, riguarda il fatto che tutte le operazioni sono vincolate a interi, mentre le altre estensioni SIMD, a eccezione delle MMX, consentono di lavorare su valori in virgola mobile a singola precisione (32 bit), o addirittura doppia (64 bit), che risultano estremamente utili.
D’altra parte ricordo ancora una volta che lo scopo di ARM non è quello di andare a competere con soluzioni molto più mature e mirate alle pure prestazioni, quanto quello di offrire ai licenziatari delle sue licenze un core molto versatile che può essere impiegato anche per scopi diversi dall’elaborazione general purpose, mantenendo al contempo bassi i costi (non servono DSP o processori dedicati).