2010-06-29 21 views
8

Mi sono imbattuto in un curioso problema. Un algoritmo Sto lavorando su è costituito da un sacco di calcoli come questoprestazioni a virgola mobile a 32 bit rispetto a 64 bit

q = x(0)*y(0)*z(0) + x(1)*y(1)*z(1) + ... 

in cui la lunghezza della somma è compresa tra 4 e 7.

I calcoli originali sono tutti fatti con 64 bit di precisione. Per la sperimentazione, ho provato a utilizzare la precisione a 32 bit per i valori di input x, y, z (in modo che i calcoli vengano eseguiti utilizzando 32 bit) e memorizzare il risultato finale come valore a 64 bit (cast diretto).

mi aspettavo prestazioni a 32 bit per essere meglio (dimensione della cache, SIMD dimensioni, ecc), ma con mia sorpresa non c'era alcuna differenza in termini di prestazioni, forse anche diminuire.

L'architettura in questione è Intel 64, Linux e GCC. Entrambi i codici sembrano utilizzare SSE e le matrici in entrambi i casi sono allineate al limite di 16 byte.

Perché dovrebbe essere così? La mia ipotesi è che la precisione a 32 bit può usare SSE solo sui primi quattro elementi, mentre il resto viene composto in serie dall'overhead del cast.

+0

Hai aggiunto una taglia - cosa non ti è piaciuto della risposta di dsimcha? Potrebbe anche valere la pena provare il GCC più recente che puoi o il compilatore di Intel http://software.intel.com/en-us/articles/non-commercial-software-download/ per vedere se fanno un miglior lavoro di compilazione/vettorizzazione . – Rup

+0

@Rup Mi piace la sua risposta, tuttavia vorrei anche altre opinioni, quindi metto una taglia – Anycorn

risposta

24

Su almeno x87, tutto è veramente fatto con precisione a 80 bit internamente. La precisione determina in realtà quanti di quei bit sono memorizzati in memoria. Questo fa parte del motivo per cui le diverse impostazioni di ottimizzazione possono modificare leggermente i risultati: cambiano la quantità di arrotondamento da 80 bit a 32 o 64 bit.

In pratica, utilizzando virgola mobile 80-bit (long double in C e C++, real in D) è generalmente lenta perché non c'è modo efficiente per caricare e memorizzare 80 bit dalla memoria. 32 e 64 bit di solito sono ugualmente veloci purché la larghezza di banda della memoria non sia il collo di bottiglia, cioè se tutto è comunque nella cache. 64 bit può essere più lento se si verifica una delle seguenti situazioni:

  1. La larghezza di banda della memoria è il collo di bottiglia.
  2. I numeri a 64 bit non sono allineati correttamente sui limiti di 8 byte. I numeri a 32 bit richiedono solo un allineamento a 4 byte per un'efficienza ottimale, quindi sono meno schizzinosi. Alcuni compilatori (il compilatore Digital Mars D mi viene in mente) non sempre lo fanno bene per i doppi a 64 bit memorizzati nello stack. Ciò causa il doppio del numero di operazioni di memoria necessarie per caricarne una, in pratica con un risultato di prestazioni pari a circa 2 volte rispetto ai galleggianti a 64 bit o ai galleggianti a 32 bit correttamente allineati.

Per quanto riguarda le ottimizzazioni SIMD, è necessario notare che la maggior parte dei compilatori è orribile nel codice auto-vettoriale. Se non si desidera scrivere direttamente in linguaggio assembly, il modo migliore per trarre vantaggio da queste istruzioni consiste nell'utilizzare operazioni come le operazioni array-based, che sono disponibili, ad esempio, in D e implementate in termini di istruzioni SSE. Allo stesso modo, in C o C++, probabilmente vorrai usare una libreria di funzioni di alto livello ottimizzata per SSE, anche se non ne conosco una buona perché ho programmato principalmente in D.

+4

"x87" - Leggermente migliore rispetto ai vecchi processori x86. :-) – Thanatos

+4

http://en.wikipedia.org/wiki/X87 – Adam

0

Probabilmente è perché il processore esegue ancora il conteggio a 64 bit e quindi ritaglia il numero. C'era qualche bandiera della CPU che potevi cambiare, ma non ricordo ...

0

Prima controlla l'ASM che viene prodotto. Potrebbe non essere quello che ti aspetti.

correlate scriverlo come un ciclo:

typedef float fp; 
fp q = 0 
for(int i = 0; i < N; i++) 
    q += x[i]*y[i]*z[i] 

Alcuni compilatore potrebbe notare il ciclo e non la forma srotolato.

Infine, il codice utilizzato () anziché []. Se il tuo codice sta effettuando molte chiamate di funzione (da 12 a 21), questo impoverirà il costo di FP e persino la rimozione del calcolo dell'FP non farà molta differenza. In linea con OTOH potrebbe.

+0

grazie, in realtà 'q()' sono le macro che convertono direttamente all'accesso puntatore raw – Anycorn

+0

@aaa: Beh, se c'è qualche matematica, potrebbe ancora essere una grande percentuale Inoltre, non so quanto bene il compilatore si occupi di mixare FP e altre cose. Questo potrebbe essere sufficiente per impedirgli di utilizzare le operazioni di vettore. – BCS

Problemi correlati