2012-11-20 9 views
6

Sto cercando di estrarre 4 byte da un registro a 128 bit in modo efficiente. Il problema è che ogni valore è in uno sperato 32 bit {120,0,0,0,55,0,0,0,42,0,0,0,120,0,0,0}. Voglio trasformare il 128 bit in 32 bit nel modulo {120,55,42,120}.Estrazione di SSE mischiato a 32 bit con solo SSE2

Il codice "raw" è simile al seguente:

__m128i byte_result_vec={120,0,0,0,55,0,0,0,42,0,0,0,120,0,0,0}; 
unsigned char * byte_result_array=(unsigned char*)&byte_result_vec; 
result_array[x]=byte_result_array[0]; 
result_array[x+1]=byte_result_array[4]; 
result_array[x+2]=byte_result_array[8]; 
result_array[x+3]=byte_result_array[12]; 

Il mio codice è SSSE3:

unsigned int * byte_result_array=...; 
__m128i byte_result_vec={120,0,0,0,55,0,0,0,42,0,0,0,120,0,0,0}; 
const __m128i eight_bit_shuffle_mask=_mm_set_epi8(1,1,1,1,1,1,1,1,1,1,1,1,0,4,8,12);  
byte_result_vec=_mm_shuffle_epi8(byte_result_vec,eight_bit_shuffle_mask); 
unsigned int * byte_result_array=(unsigned int*)&byte_result_vec; 
result_array[x]=byte_result_array[0]; 

Come posso fare questo in modo efficiente con SSE2. Esiste una versione migliore con SSSE3 o SSE4?

risposta

9

È possibile controllare a previous answer of mine per alcune soluzioni a questa e all'operazione inversa.

In particolare nel SSE2 è possibile farlo in primo luogo l'imballaggio gli interi a 32-bit in firmati interi a 16 bit e saturazione:

byte_result_vec = _mm_packs_epi32(byte_result_vec, byte_result_vec); 

Poi abbiamo imballare i valori a 16 bit in valori a 8 bit senza segno utilizzando saturazione senza segno:

byte_result_vec = _mm_packus_epi16(byte_result_vec, byte_result_vec); 

possiamo quindi finalmente prendere i nostri valori dai inferiori 32-bit del registro:

int int_result = _mm_cvtsi128_si32(byte_result_vec); 
unsigned char* byte_result_array = (unsigned char*)&int_result; 
result_array[x] = byte_result_array[0]; 
result_array[x+1] = byte_result_array[1]; 
result_array[x+2] = byte_result_array[2]; 
result_array[x+3] = byte_result_array[3]; 

MODIFICA: Quanto sopra presuppone che le parole di 8 bit siano inizialmente nei byte bassi delle rispettive parole a 32 bit e il resto sia riempito con 0 s, poiché altrimenti esse verranno bloccate durante il processo di imballaggio in saturazione. Così le operazioni sono le seguenti:

   byte 15        0 
        0 0 0 D 0 0 0 C 0 0 0 B 0 0 0 A 

_mm_packs_epi32 -> 0 D 0 C 0 B 0 A 0 D 0 C 0 B 0 A 

_mm_packus_epi16 -> D C B A D C B A D C B A D C B A 
               ^^^^^^^ 

_mm_cvtsi128_si32 -> int DCBA, laid out in x86 memory as bytes A B C D 

-> reinterpreted as unsigned char array { A, B, C, D } 

Se i byte uninterresting non sono pieni di 0 s inizialmente, bisogna mascherare via in anticipo:

byte_result_vec = _mm_and_si128(byte_result_vec, _mm_set1_epi32(0x000000FF)); 

O se i byte interresting sono inizialmente nella alti byte, si devono spostare in loro bassi byte in anticipo:

byte_result_vec = _mm_srli_epi32(byte_result_vec, 24); 

O, se si vuole realmente { D, C, B, A } (che non è compl etely clear to me dalla tua domanda), beh, allora questo equivale a cambiare semplicemente l'indice dell'array nei compiti assegnati (o, in alternativa, a perfezionare uno shuffle a 32 bit (_mm_shuffle_epi32) sul registro SSE iniziale in anticipo).

+0

questa è una risposta perfetta. Come posso revocarlo due volte? :) questo mi ha aiutato molto Ti capita di conoscere un modo migliore con SSE4? –

+0

@martins: con SSSE3 e versioni successive, si desidera solo un PSHUFB (che è ciò che il codice esistente deve compilare). –

+0

@martins Non sono molto esperto in SSE> 2, forse cercherò di esaminarlo. –

Problemi correlati