Vettorizzazione automatica su doppia e ffast-math
-
27-09-2019 - |
Domanda
Perché è obbligatorio da usare -ffast-math
con G ++ per ottenere la vettorializzazione dei loop usando double
S? Non mi piace -ffast-math
Perché non voglio perdere la precisione.
Soluzione
Non perdi necessariamente la precisione con -ffast-math
. Influisce solo sulla gestione di NaN
, Inf
ecc. e l'ordine in cui vengono eseguite le operazioni.
Se si dispone di un codice specifico in cui non si desidera che GCC riordini o semplifica i calcoli, è possibile contrassegnare le variabili come utilizzate utilizzando un asm
dichiarazione.
Ad esempio, il seguente codice esegue un'operazione di arrotondamento su f
. Tuttavia, i due f += g
e f -= g
È probabile che le operazioni vengano ottimizzate da GCC:
static double moo(double f, double g)
{
g *= 4503599627370496.0; // 2 ** 52
f += g;
f -= g;
return f;
}
Su x86_64, puoi usarlo asm
Dichiarazione per istruire GCC di non eseguire tale ottimizzazione:
static double moo(double f, double g)
{
g *= 4503599627370496.0; // 2 ** 52
f += g;
__asm__("" : "+x" (f));
f -= g;
return f;
}
Dovrai adattarlo per ogni architettura, sfortunatamente. Su powerPC, usa +f
invece di +x
.
Altri suggerimenti
Molto probabilmente perché la vettorializzazione significa che potresti avere risultati diversi o potrebbe significare che si perdono segnali/eccezioni di punti galleggianti.
Se stai compilando per X86 a 32 bit, allora GCC e G ++ default a utilizzare l'X87 per il punto matematico mobile, su 64 bit, sono impostati predefiniti su SSE, tuttavia X87 può e produce valori diversi per lo stesso calcolo prendere in considerazione la vettorializzazione se non può garantire che otterrai gli stessi risultati a meno che non si usa -ffast-math
O alcune delle bandiere che si accendono.
Fondamentalmente si riduce all'ambiente a punta mobile per il codice vettoriale potrebbe non essere lo stesso di quello per il codice non vettoriale, a volte in modi importanti, se le differenze non contano, qualcosa come
-fno-math-errno -fno-trapping-math -fno-signaling-nans -fno-rounding-math
Ma prima cerca quelle opzioni e assicurati che non influenzino la correttezza del programma. -ffinite-math-only
può anche aiutare
Perché -ffast-math
consente operandi riordinamento che consente a molti codice di essere vettoriali.
Ad esempio per calcolare questo
sum = a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + … a[99]
Il compilatore è necessario Per fare le aggiunte sequenzialmente senza -ffast-math
, perché la matematica a punta mobile non è né commutativa né associativa.
- L'aggiunta fluttuante è commutativo e associativo?
- L'aggiunta di punto galleggiante è commutativo in C ++?
- Le operazioni di punta mobile sono in C Association?
- L'aggiunta di punto galleggiante e l'associazione di moltiplicazione?
Questo è lo stesso motivo Perché i compilatori non possono ottimizzare a*a*a*a*a*a
a (a*a*a)*(a*a*a)
senza -ffast-math
Ciò significa che non è disponibile alcuna vettorializzazione a meno che non si disponga di un vettore orizzontale molto efficiente.
Tuttavia se -ffast-math
è abilitato, l'espressione può essere calcolata come questo (Guarda a A7. Auto-Vectorization
)
sum0 = a[0] + a[4] + a[ 8] + … a[96]
sum1 = a[1] + a[5] + a[ 9] + … a[97]
sum2 = a[2] + a[6] + a[10] + … a[98]
sum3 = a[3] + a[7] + a[11] + … a[99]
sum’ = sum0 + sum1 + sum2 + sum3
Ora il compilatore può vettoriale facilmente aggiungendo ogni colonna in parallelo e quindi fare un aggiunto orizzontale alla fine
Fa
sum’ == sum
? Solo se(a[0]+a[4]+…) + (a[1]+a[5]+…) + (a[2]+a[6]+…) + ([a[3]+a[7]+…) == a[0] + a[1] + a[2] + …
Questo è sotto associazione, che galleggia non aderisce, tutto il tempo. Specifica/fp:fast
Consentono al compilatore di trasformare il codice in modo più veloce - fino a 4 volte più veloce, per questo semplice calcolo.Preferisci veloce o preciso? - A7. Auto-vettorializzazione
Può essere abilitato da -fassociative-math
Flag in GCC