2013-12-11 15 views
5

Ho un'espressione booleana che sono riuscito a implementare in SSE2. Ora mi sarebbe piaciuto provare a implementarlo in AVX sfruttando un fattore aggiuntivo 2 nell'aumento del parallelismo (da 128 bit tipo SIMD a 256). Tuttavia, AVX non supporta l'operazione integer (che funziona con AVX2, ma sto lavorando su un processore Sandy Bridge quindi non è attualmente un'opzione). Tuttavia, poiché ci sono AVX intrinsics for bitwise operations. Ho pensato che potevo provare semplicemente convertendo i miei tipi interi in tipi mobili e vedere se funziona.Esiste un modo per simulare operazioni bit a bit su interi per _m256 tipi su AVX?

primo test è stato un successo:

__m256 ones = _mm256_set_ps(1,1,1,1,1,1,1,1); 
__m256 twos = _mm256_set_ps(2,2,2,2,2,2,2,2); 
__m256 result = _mm256_and_ps(ones, twos); 

sto Guetting tutti i 0 di come dovrei. Simularly AND'ing i due a due invece che ottenere un risultato di 2. Ma quando si cerca 11 XOR 4 conseguenza:

__m256 elevens = _mm256_set_ps(11,11,11,11,11,11,11,11); 
__m256 fours = _mm256_set_ps(4,4,4,4,4,4,4,4); 
__m256 result2 = _mm256_xor_ps(elevens, fours); 

Il risultato è 6.46e-46 (cioè vicino a 0) e non 15. Simularly fare 11 OR 4 mi dà un valore di 22 e non 15 come dovrebbe essere. Non capisco perché sia ​​così. È un bug o una configurazione che mi manca?

In realtà mi aspettavo la mia ipotesi di lavorare con float come se fossero interi per non funzionare poiché il numero intero inizializzato su un valore float potrebbe non essere effettivamente il valore preciso ma una stretta approssimazione. Ma anche allora, sono sorpreso dal risultato che ottengo.

Qualcuno ha una soluzione a questo problema o devo aggiornare la mia CPU per ottenere supporto AVX2 abilitare questo?

+0

Sembra che tu stia stampando un numero intero come float per ottenere 6.46e-46. Sei sicuro che i tuoi specificatori di formattazione 'printf()' sono corretti? –

+0

Non stavo stampando. Ho appena controllato il valore nel debugger di Visual Studio. – Toby999

risposta

7

Il primo test ha funzionato accidentalmente.

1 come float è 0x3f800000, 2 è 0x40000000. In generale, non funzionerebbe in questo modo.

Ma puoi assolutamente farlo, devi solo assicurarti di lavorare con il modello di bit corretto. Non convertire i tuoi numeri interi in float, reinterpretandoli. Ciò corrisponde a intrinseche come _mm256_castsi256_ps, o memorizza i tuoi in memoria e li legge come float (che non li cambierà, in generale solo le operazioni matematiche si preoccupano di ciò che significa float significa, il resto funziona con i modelli di bit grezzi, controlla l'elenco delle eccezioni che un'istruzione può fare per essere sicuro).

+0

Aha. Grazie. Ciò ha senso. Ci provo e segna la tua risposta come corretta se funziona. – Toby999

+2

@ Toby999 Tuttavia, tenere presente che su tutti gli attuali processori Intel, le versioni a virgola mobile delle istruzioni di logica bit a bit hanno solo 1/3 del throughput delle versioni integer. Quindi, se stai facendo questo per le prestazioni, potresti voler pensarci due volte. Potrebbe persino ritorcersi contro se non si è limitati dalla larghezza di banda del decodificatore. – Mysticial

+2

Su Sandy e Ivy Bridge, la logica bit a bit SSE intera può passare a qualsiasi porta 0, 1 o 5 a uno/ciclo. Questo è 3 per ciclo.Ma la logica bitwise SSE a virgola mobile può solo andare alla porta 5 in un ciclo /. Quindi è limitato a 1 per ciclo. Su Haswell, è lo stesso, ma ha AVX2 - il che rende il punto discutibile. – Mysticial

4

Non è necessario AVX2 per utilizzare il carico intero AVX e le operazioni di memorizzazione: vedere lo intel intrinsic guide. Quindi puoi caricare i tuoi numeri interi usando AVX, reinterpretare il cast in float, usare le operazioni bitwise float e quindi reinterpretare il cast a int. I reinterpret-cast non generano alcuna istruzione, rendono semplicemente il compilatore felice. Prova questo:

//compiled and ran on an Ivy Bridge system with AVX but without AVX2 
#include <stdio.h> 
#include <immintrin.h> 
int main() { 
    int a[8] = {0, 2, 4, 6, 8, 10, 12, 14}; 
    int b[8] = {1, 1, 1, 1, 1, 1, 1, 1}; 
    int c[8]; 

    __m256i a8 = _mm256_loadu_si256((__m256i*)a); 
    __m256i b8 = _mm256_loadu_si256((__m256i*)b); 
    __m256i c8 = _mm256_castps_si256(
     _mm256_or_ps(_mm256_castsi256_ps(a8), _mm256_castsi256_ps(b8))); 
    _mm256_storeu_si256((__m256i*)c, c8); 
    for(int i=0; i<8; i++) printf("%d ", c[i]); printf("\n"); 
    //output: 1 3 5 7 9 11 13 15 
} 

Naturalmente, come mistico ha sottolineato questo potrebbe non essere la pena di fare, ma che non significa che non si può fare.

+0

Grazie per l'input.) È stato utile poiché richiede molto tempo scavare i metodi intrinseci corretti. – Toby999

+0

ci sono opzioni per allineare le variabili in modo da non dover gestire carichi non allineati –

+0

@ LưuVĩnhPhúc, stavo lavorando con Supponendo che non abbia più importanza, il throughput e la latenza delle istruzioni di caricamento/immagazzinamento allineate e non allineate sono le stesse sulla memoria allineata, questa è la teoria, ma in pratica sto ancora vedendo una differenza, quindi sono d'accordo con te dovrebbero essere usate istruzioni di caricamento allineate. –

Problemi correlati