2014-07-06 8 views
8

Ho seguente problema:permutando byte all'interno SSE __m128i registrare

In __m128i registro ci sono 16 valori 8bit nel seguente ordine:

[ 1, 5, 9, 13 ] [ 2, 6, 10, 14] [3, 7, 11, 15] [4, 8, 12, 16] 

Quello che vorrei realizzare è mescolare in modo efficiente byte per ottenere questo ordinamento:

[ 1, 2, 3, 4 ] [ 5, 6, 7, 8] [9, 10, 11, 12] [13, 14, 15, 16] 

realtà è analogico a 4x4 matrice trasposizione, ma operando sull'elemento 8-bit all'interno di uno r egister.

Per favore, puoi indicarmi che tipo di istruzioni SSE (preferibilmente < = SSE2) sono adatte per la realizzazione di questo?

+2

Bene con SSSE3 c'è 'pshufb', altrimenti si sta per ottenere un po 'disordinato – harold

+0

Grazie per la segnalazione a me pshufb! Penso di poter passare da lì :) – born49

+0

Oltre a 'PSHUFB' di SSSE3, che è una sorta di esclusiva Intel su CPU più datate, si sta osservando una sequenza disordinata di istruzioni SSE2' PUNPCK (L/H) BW', come @harold ha sottolineato. –

risposta

5

È davvero si vuole andare SSSE3 per questo, è molto più pulito che cercare di andare < = SSE2

il codice sarà simile a questa:

#include <tmmintrin.h> // _mm_shuffle_epi8 
    #include <tmmintrin.h> // _mm_set_epi8 
    ... 
    // check if your hardware supports SSSE3 
    ... 
    __m128i mask = _mm_set_epi8(15, 11, 7, 3, 
           14, 10, 6, 2, 
           13, 9, 5, 1, 
           12, 8, 4, 0); 
    __m128i mtrx = _mm_set_epi8(16, 12, 8, 4, 
           15, 11, 7, 3, 
           14, 10, 6, 2, 
           13, 9, 5, 1); 
    mtrx   = _mm_shuffle_epi8(mtrx, mask); 

Se si vuole veramente SSE2 questo sarà sufficiente:
(supponendo che sto interpretando correttamente la vostra ordinazione iniziale)

__m128i mask = _mm_set_epi8(0x00, 0xFF, 0x00, 0xFF, 
           0x00, 0xFF, 0x00, 0xFF, 
           0x00, 0xFF, 0x00, 0xFF, 
           0x00, 0xFF, 0x00, 0xFF); 
    __m128i mtrx = _mm_set_epi8(16, 12, 8, 4, 
           15, 11, 7, 3, 
           14, 10, 6, 2, 
           13, 9, 5, 1);         // [1, 5, 9, 13] [2, 6, 10, 14] [3, 7, 11, 15] [ 4, 8, 12, 16] 
    mtrx = _mm_packus_epi16(_mm_and_si128(mtrx, mask), _mm_srli_epi16(mtrx, 8)); // [1, 9, 2, 10] [3, 11, 4, 12] [5, 13, 6, 14] [ 7, 15, 8, 16] 
    mtrx = _mm_packus_epi16(_mm_and_si128(mtrx, mask), _mm_srli_epi16(mtrx, 8)); // [1, 2, 3, 4] [5, 6, 7, 8] [9, 10, 11, 12] [13, 14, 15, 16] 

O più easil y debuggable:

__m128i mtrx = _mm_set_epi8(16, 12, 8, 4, 
           15, 11, 7, 3, 
           14, 10, 6, 2, 
           13, 9, 5, 1);   // [1, 5, 9, 13] [ 2, 6, 10, 14] [ 3, 7, 11, 15] [ 4, 8, 12, 16] 
    __m128i mask = _mm_set_epi8(0x00, 0xFF, 0x00, 0xFF, 
           0x00, 0xFF, 0x00, 0xFF, 
           0x00, 0xFF, 0x00, 0xFF, 
           0x00, 0xFF, 0x00, 0xFF); 
    __m128i temp = _mm_srli_epi16(mtrx, 8);    // [5, 0, 13, 0] [ 6, 0, 14, 0] [ 7, 0, 15, 0] [ 8, 0, 16, 0] 
    mtrx   = _mm_and_si128(mtrx, mask);   // [1, 0, 9, 0] [ 2, 0, 10, 0] [ 3, 0, 11, 0] [ 4, 0, 12, 0] 
    mtrx   = _mm_packus_epi16(mtrx, temp);   // [1, 9, 2, 10] [ 3, 11, 4, 12] [ 5, 13, 6, 14] [ 7, 15, 8, 16] 
    temp   = _mm_srli_epi16(mtrx, 8);    // [9, 0, 10, 0] [11, 0, 12, 0] [13, 0, 14, 0] [15, 0, 16, 0] 
    mtrx   = _mm_and_si128(mtrx, mask);   // [1, 0, 2, 0] [ 3, 0, 4, 0] [ 5, 0, 6, 0] [ 7, 0, 8, 0] 
    mtrx   = _mm_packus_epi16(mtrx, temp);   // [1, 2, 3, 4] [ 5, 6, 7, 8] [ 9, 10, 11, 12] [13, 14, 15, 16] 
+0

Mille grazie per la tua spiegazione! Userò SSE3 come percorso principale e ripiego alla versione SSE2 su piattaforme senza SSE3. – born49

+0

@ user3809354: Nessun problema! Tieni presente che dovrai chiamare CPUID una volta all'avvio e archiviare tutto ciò che ti serve per determinare quale versione del codice eseguire. [MSDN] (http://msdn.microsoft.com/en-us/library/y0dh78ez (vs80) .aspx) è un buon punto di partenza per vedere cosa c'è. Puoi navigare nell'albero per vedere gli intrinseci SSSE3 e SSE4. Qualcosa su cui ho trovato potrebbe davvero usare esempi (ad esempio significati del valore della maschera shuffle), ma questo è ciò che SO è per. :) – Apriori

+0

@ user3809354: btw, mi sono reso conto che puoi salvare un paio di istruzioni nella versione SSE2 chiamando _mm_srli_epi16 invece di _mm_srli_si128, perché si sposterà in zeri per ogni componente a 16 bit. Ciò evita una maschera successiva poiché gli zeri devono essere nel byte alto di ciascun componente a 16 bit prima di chiamare il pacchetto. Questo perché non esiste una versione di pack che tronca (ad esempio, _mm_packs_epi16 e _mm_packus_epi16 eseguono rispettivamente saturazione firmata e unsigned) in modo da gettare via il byte superiore, deve contenere zeri. Ho aggiornato il codice della risposta per riflettere questo. – Apriori

Problemi correlati