2015-09-11 12 views
6

In AVX c'è solo 128 bit PSHUFBAVX2 in AVX

VPSHUFB xmm1, xmm2, xmm3/m128 

e solo AVX2 ha la completa PSHUFB per l'intero 256 bit AVX registra

VPSHUFB ymm1, ymm2, ymm3/m256 

Come può istruzione essere emulato in modo efficiente con gli intrinsechi AVX?

Anche in questo caso particolare, la sorgente ha solo 8 elementi (byte) ma questi potrebbero essere spostati all'interno dei 32 byte completi della destinazione. Quindi non è un problema eseguire solo 2 x PSHUFB.

Un problema che sto riscontrando con VPSHUFB è che tratta 16 (0x10) come 0, solo 128 e su sono pieni di zero! (massimo bit impostato) È possibile farlo senza aggiungere confronti e mascherare?

+4

Probabilmente non l'hai notato, ma AVX2 'VPSHUFB ymm, ymm, ymm/m256' non è in realtà uno shuffle a 256 bit, invece è 2x shuffle a 128 bit. –

+0

Interessante. Grazie! – alecco

risposta

7

Come ha notato @MaratDukhan, _mm256_shuffle_epi8 (ad esempio VPSHUFB per ymm-s) non esegue la riproduzione casuale a 32 byte. Per quanto mi riguarda, è piuttosto un peccato ...

Ecco perché, al fine di emulare senza AVX2 si può semplicemente dividere ogni registro in due metà, permute ogni metà, poi si combinano insieme:

//AVX only 
__m256i _emu_mm256_shuffle_epi8(__m256i reg, __m256i shuf) { 
    __m128i reg0 = _mm256_castsi256_si128(reg); 
    __m128i reg1 = _mm256_extractf128_si256(reg, 1); 
    __m128i shuf0 = _mm256_castsi256_si128(shuf); 
    __m128i shuf1 = _mm256_extractf128_si256(shuf, 1); 
    __m128i res0 = _mm_shuffle_epi8(reg0, shuf0); 
    __m128i res1 = _mm_shuffle_epi8(reg1, shuf1); 
    __m256i res = _mm256_setr_m128i(res0, res1); 
    return res; 
} 

Se si desidera veramente mescolare il registro a 32 byte, è possibile seguire l'approccio da this paper. Mischia ogni metà con ciascuna metà, quindi mescola i risultati insieme. Senza AVX2 sarebbe qualcosa di simile:

//AVX only 
__m256i _emu_mm256_shuffle32_epi8(__m256i reg, __m256i shuf) { 
    __m128i reg0 = _mm256_castsi256_si128(reg); 
    __m128i reg1 = _mm256_extractf128_si256(reg, 1); 
    __m128i shuf0 = _mm256_castsi256_si128(shuf); 
    __m128i shuf1 = _mm256_extractf128_si256(shuf, 1); 
    __m128i res00 = _mm_shuffle_epi8(reg0, shuf0); 
    __m128i res01 = _mm_shuffle_epi8(reg0, shuf1); 
    __m128i res10 = _mm_shuffle_epi8(reg1, shuf0); 
    __m128i res11 = _mm_shuffle_epi8(reg1, shuf1); 
    __m128i res0 = _mm_blendv_epi8(res10, res00, _mm_cmplt_epi8(shuf0, _mm_set1_epi8(16))); 
    __m128i res1 = _mm_blendv_epi8(res11, res01, _mm_cmplt_epi8(shuf1, _mm_set1_epi8(16))); 
    __m256i res = _mm256_setr_m128i(res0, res1); 
    return res; 
} 

Se si sa per certo che solo la metà inferiore del reg viene utilizzato, è possibile rimuovere le linee per reg1, res10, res11, e rimuovere il confronto e la miscelazione. In effetti, potrebbe essere più efficiente attenersi a SSE e utilizzare registri a 128 bit se non si dispone di AVX2.

Il generale 32 byte rimescolamento può essere notevolmente ottimizzato con AVX2:

//Uses AVX2 
__m256i _ext_mm256_shuffle32_epi8(__m256i reg, __m256i shuf) { 
    __m256i regAll0 = _mm256_permute2x128_si256(reg, reg, 0x00); 
    __m256i regAll1 = _mm256_permute2x128_si256(reg, reg, 0x11); 
    __m256i resR0 = _mm256_shuffle_epi8(regAll0, shuf); 
    __m256i resR1 = _mm256_shuffle_epi8(regAll1, shuf); 
    __m256i res = _mm256_blendv_epi8(resR1, resR0, _mm256_cmpgt_epi8(_mm256_set1_epi8(16), shuf)); 
    return res; 
} 

Attenzione: codice non testato!

+0

Una risposta completa, molto più di quanto mi aspettassi. Molte grazie! – alecco

+0

Sei consapevole del fatto che l'implementazione solo AVX contiene '_mm256_extracti128_si256', che è disponibile solo in AVX2? – plasmacel

+0

@stgatilov Controllalo ancora, '_mm256_extracti128_si256' compila a' vextracti128', che è AVX2. Stai parlando di '_mm256_extractf128_si256', che compila in' vextractf128'. Sono simili, ma mentre 'vextractf128' opera su un dominio in virgola mobile,' vextracti128' opera sul dominio intero. – plasmacel

Problemi correlati