|
VV.com Aficionados
Data registrazione: 27-11-2007
Residenza: near COD NDB
Messaggi: 686
|
...continua...
Avevo accennato al fatto che taluni contesti applicativi possono trarre giovamento dall'uso di dati a 64 bit invece che a 32, senza dire di più. Ebbene, magari qualche parolina per fare capire meglio questo discorso credo ci possa stare.
Partiamo da una considerazione di carattere generale. Lanciare (e di conseguenza utilizzare) un programma significa, stringi stringi, far eseguire alla CPU una sequenza di istruzioni che elaborano dati in varia maniera, per poi presentare i risultati di queste elaborazioni attraverso diversi canali di output (lo schermo, le casse audio, la stampante, ecc.).
Per quanto lunga e complicata possa essere la sequenza di istruzioni eseguite, alla fin fine le categorie di operazioni eseguite dalla CPU sono sempre quelle, ovvero:
Aritmetiche: come dice il nome, si tratta delle consuete operazioni di aritmetica che abbiamo imparato a scuola: addizioni, sottrazioni, prodotti e, a seconda della CPU, anche divisioni.
Logiche: questo tipo di operazioni è intimamente legato al fatto che la rappresentazione dei dati in un calcolatore avviene secondo il sistema binario, sistema che prevede una serie di operazioni tipiche e definite appunto logiche: AND, OR, NOT, XOR, NAND, NOR, ecc.
Load/store: queste operazioni non effettuano elaborazioni vere e proprie, bensì spostano i dati tra CPU e memoria.
Controllo: queste istruzioni hanno lo scopo principale di influenzare la sequenza di esecuzione delle altre istruzioni in funzione dell'avverarsi o meno di determinate condizioni. In questo genere di istruzioni rientrano ad esempio i classici "salti condizionati", ovvero comandi del tipo "vai all'istruzione XXX se..."
Esaminiamo in particolare le istruzioni aritmetiche e logiche, che sono quelle intorno a cui ruota tutto l'impianto di elaborazione di un'applicazione. Nell'architettura x86 dei processori dei nostri PC, queste istruzioni possono operare su dati di 8 o 16 bit (in tutte le CPU x86), o 32 bit (dal 386 in poi), oppure 64 bit (dall'Athlon64 e Pentium 4 serie 600 in poi, purché inizializzati per funzionare in modalità a 64 bit: questa inizializzazione è compito del sistema operativo). Allora, quand'è che abbiamo bisogno dei 64 bit?
Facciamo un passo indietro. Si diceva che 1 bit, che è l'unità più piccola di informazione che una CPU può elaborare, rappresenta solo due possibili valori, 0 e 1. Con 2 bit posso rappresentare 2*2=4 valori, con 3 bit 8 valori, e via di seguito. Con dati di 16 bit (gli unici che si potevano processare direttamente con le CPU x86 fino all'80286) posso rappresentare 2^16 = 65536 valori. A seconda di come uso questa rappresentazione, con 16 bit posso individuare un numero intero senza segno da 0 a 65535, oppure un numero intero con segno da -32768 a 32767.
A questo punto inizierete ad intuire il problema, ovvero: cosa succede se io devo fare dei conti i cui dati e risultati siano più grandi di questi valori massimi che posso rappresentare con 16 bit? Mi attacco? Beh, non proprio. Posso comunque fare i miei conti, ma devo "spezzarli" in più parti le quali possano essere portate avanti con numeri che rientrino nei valori rappresentabili in 16 bit. Ed ecco il fulcro del problema: se voglio far fare alla mia CPU calcoli su valori che superano il limite rappresentabile, posso farlo ma al costo di una sequenza più o meno lunga di istruzioni, invece che usarne una sola.
Senza star qui a dimostrarlo rigorosamente, però posso farvi questo esempio: supponiamo di voler moltiplicare tra loro due numeri di 16 bit. Il risultato sarà un numero di 32 bit. Con una CPU x86 a 32 bit possiamo fare questa operazione con un'unica istruzione aritmetica e 1 scrittura in memoria; con un ipotetico 80286, per ottenere lo stesso risultato, dovremmo invece fare 1 moltiplicazione e 2 scritture in memoria (visto che una CPU a 16 bit può leggere/scrivere 16 bit alla volta). E già per questo semplice esempio vediamo che una CPU a 32 bit deve fare un'operazione in meno (2 contro 3), con un risparmio del 33% sul tempo di esecuzione. Senza contare poi se il risultato di questa moltiplicazione dovesse venire impiegato in successive operazioni aritmetiche (per esempio una somma): nel caso della CPU a 32 bit potremmo direttamente usare il risultato in tutti i suoi 32 bit e fare la nostra somma; con una CPU a 16 bit dovremmo spezzare la somma a 32 bit in due somme da 16, che poi diventano 3 somme se teniamo conto che sommando i 16 bit che rappresentano la metà inferiore dei 32 bit potremmo ottenere un bit di riporto che andrebbe poi a sua volta sommato nei 16 bit della metà superiore.
Insomma, operazioni aritmetiche che superino la precisione massima supportata da una CPU sono sì fattibili, ma diventano un macello, con un peso prestazionale non indifferente.
Un'alternativa comoda per il programmatore è quella di usare i dati in virgola mobile (floating point) anche per fare calcoli su numeri interi. A partire dal Pentium Intel e dal K6 AMD, tutte le CPU hanno integrata una FPU (Floating Point Unit), ovvero un co-processore il cui scopo è quello di gestire specificamente i calcoli di numeri con la virgola. I numeri floating point permettono la rappresentazione di un range di valori enormemente più esteso di quelli rappresentabili con un intero. Ma a che prezzo: anche operazioni banali come le somme, se fatte con la FPU, sono più lente delle corrispondenti operazioni su interi. L'uso della FPU, quindi, è di fatto relegato solamente agli ambiti in cui la precisione dei calcoli è prioritaria rispetto alla velocità, e quindi parliamo dei calcoli scientifici, del rendering 3D, tanto per dirne due.
Ecco perché alcune applicazioni possono essere più veloci se si hanno a disposizione operazioni in grado di agire su dati a 64 bit invece che a 32: si tratta di risparmiare istruzioni, e fare quindi lo stesso conto più speditamente.
Senza contare che poter processare numeri interi a 64 bit apre un'altra interessante possibilità, ovvero (per talune applicazioni sulle quali la precisione è sì richiesta ma non è un must assoluto, e quindi ci si "accontenta" di precisioni inferiori a quelle della virgola mobile) l'uso della rappresentazione cosiddetta FIXED POINT. In pratica si tratta di dire: posso fare operazioni aritmetiche su numeri a 64 bit? Bene, allora dico che di questi 64 bit ne uso (per fare un esempio) 32 per rappresentare la parte intera e gli altri 32 per la parte decimale di un numero. Così facendo posso rappresentare, pur usando un numero intero, numeri con la virgola con una precisione di 2^(-32) = 0,00000000023 = 2,3*10^(-10) cioè un numero decimale con 9 cifre significative dopo la virgola, che non è poco. In più, con gli altri 32 bit posso rappresentare 2^32 = 4294967296 valori. Quindi con un numero Fixed Point a 64 bit di cui 32 per la parte intera e 32 per quella decimale posso rappresentare valori con la virgola che vanno da 0 a 4294967295,99999999977.
Con questa furbata posso permettermi di fare conti su numeri con la virgola (con i limiti di precisione che abbiamo appena visto in questo esempio) senza usare la FPU che è più lenta. Avendo a disposizione CPU a 32 bit, l'uso di questo sistema sarebbe stato meno raccomandabile: se dei 32 bit decidevo (per esempio) di destinarne 16 alla parte intera e gli altri 16 alla parte decimale, potevo rappresentare valori da 0 a 65535,999984, con una precisione di 0,000016 che è chiaramente mediocre, sicuramente insufficiente per molte applicazioni in ambito scientifico.
So che sono andato a scavare un argomento piuttosto ostico, che è quello delle approssimazioni dovute alla precisione finita della rappresentazione dei dati, però spero che questo possa far capire in che situazioni avere la possibilità di processare molti bit può tornare utile.
|