2015-09-11 7 views
6

In AVX gibt es nur 128 Bit PSHUFBAVX2 VPSHUFB Emulation in AVX

VPSHUFB xmm1, xmm2, xmm3/m128 

und nur AVX2 die vollen PSHUFB für den gesamten 256 Bit AVX registriert hat

VPSHUFB ymm1, ymm2, ymm3/m256 

Wie diese Anweisung emuliert werden kann effizient mit AVX-Intrinsics?

Auch in diesem speziellen Fall hat die Quelle nur 8 Elemente (Bytes), aber diese könnten innerhalb der vollen 32 Bytes des Ziels verschoben werden. Es ist also kein Problem, nur 2 x PSHUFB zu laufen.

Ein Problem, das ich mit VPSHUFB finde, ist es behandelt 16 (0x10) als 0, nur 128 und höher sind mit Null gefüllt! (höchstes gesetztes Bit) Ist es möglich, dies ohne Hinzufügen von Vergleichen und Ausblenden zu tun?

+4

Wahrscheinlich haben Sie nicht bemerkt, aber AVX2 'VPSHUFB ymm, ymm, ymm/m256' ist nicht wirklich eine 256-Bit-Shuffle, sondern es ist 2x 128-Bit-Shuffle. –

+0

Interessant. Vielen Dank! – alecco

Antwort

7

Wie @MaratDukhan festgestellt hat, führt _mm256_shuffle_epi8 (d. H. VPSHUFB für ymm-s) nicht die vollständige 32-Byte-Zufallswiedergabe durch. Was mich betrifft, es ist ein ziemlich schade ...

, deshalb, um es ohne AVX2 zu emulieren Sie einfach jede geteilt in zwei Hälften registrieren, permute jede Hälfte, dann zusammen kombinieren:

//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; 
} 

Wenn Sie das 32-Byte-Register wirklich vollständig mischen möchten, können Sie die Vorgehensweise von this paper aus verfolgen. Mischen Sie jede Hälfte mit jeder Hälfte und mischen Sie dann die Ergebnisse zusammen.

//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; 
} 

Wenn Sie sicher sind, dass nur die untere Hälfte des reg verwendet wird, dann können Sie Linien für reg1 entfernen, res10, res11, und entfernen Sie den Vergleich und die Mischung: Ohne AVX2 wäre es so ähnlich sein. In der Tat könnte es effizienter sein, mit SSE zu bleiben und 128-Bit-Register zu verwenden, wenn Sie kein AVX2 haben.

Das allgemeine 32-Byte-Shuffling signifikant mit AVX2 optimiert werden kann:

//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; 
} 

Vorsicht: Code nicht getestet!

+0

Eine gründliche Antwort, viel mehr als ich erwartet hatte. Danke vielmals! – alecco

+0

Ist Ihnen bekannt, dass Ihre AVX-Implementierung '_mm256_extracti128_si256' enthält, die nur in AVX2 verfügbar ist? – plasmacel

+0

@stgatilov Überprüfen Sie es erneut, '_mm256_extracti128_si256' kompiliert zu' vextrtrati128', das ist AVX2. Sie sprechen über '_mm256_extractf128_si256', das zu' vextractf128' kompiliert wird. Sie sind ähnlich, aber während 'vextractf128' auf Gleitkommadomänen arbeitet, arbeitet' vectracti128' in ganzzahligen Domänen. – plasmacel