2011-12-15 15 views
7

W SSE3, instrukcja PALIGNR wykonuje następujące czynności:_mm_alignr_epi8 (PALIGNR) równowartość w AVX2

PALIGNR concatenates the destination operand (the first operand) and the source operand (the second operand) into an intermediate composite, shifts the composite at byte granularity to the right by a constant immediate, and extracts the right-aligned result into the destination.

Jestem aktualnie w środku przenoszenie mojego kodu SSE4 użyć instrukcji AVX2 i pracy na 256bit rejestruje zamiast 128-bitowy. Naiwnie wierzyłem, że funkcja wewnętrzna _mm256_alignr_epi8 (VPALIGNR) wykonuje tę samą operację co _mm_alignr_epi8 tylko w rejestrach 256-bitowych. Niestety jednak tak nie jest. W rzeczywistości, _mm256_alignr_epi8 traktuje rejestr 256-bitowy jako 2 128-bitowe rejestry i wykonuje 2 operacje "wyrównania" na dwóch sąsiednich rejestrach 128-bitowych. Skutecznie wykonuje tę samą operację co _mm_alignr_epi8, ale na 2 rejestrach jednocześnie. To najbardziej wyraźnie przedstawiono tutaj: _mm256_alignr_epi8

Obecnie moje rozwiązanie jest utrzymanie przy użyciu _mm_alignr_epi8 dzieląc ymm (256bit) rejestrach na dwie XMM (128bit) rejestrów (wysokie i niskie), tak jak poniżej:

__m128i xmm_ymm1_hi = _mm256_extractf128_si256(ymm1, 0); 
__m128i xmm_ymm1_lo = _mm256_extractf128_si256(ymm1, 1); 
__m128i xmm_ymm2_hi = _mm256_extractf128_si256(ymm2, 0); 
__m128i xmm_ymm_aligned_lo = _mm_alignr_epi8(xmm_ymm1_lo, xmm_ymm1_hi, 1); 
__m128i xmm_ymm_aligned_hi = _mm_alignr_epi8(xmm_ymm2_hi, xmm_ymm1_lo, 1); 
__m256i xmm_ymm_aligned = _mm256_set_m128i(xmm_ymm_aligned_lo, xmm_ymm_aligned_hi); 

To działa, ale musi być lepszy sposób, prawda? Czy jest być może więcej "ogólnych" instrukcji AVX2, które powinny być używane, aby uzyskać ten sam wynik?

Odpowiedz

2

Jedynym rozwiązaniem udało mi się wymyślić na to:

static inline __m256i _mm256_alignr_epi8(const __m256i v0, const __m256i v1, const int n) 
{ 
    if (n < 16) 
    { 
    __m128i v0h = _mm256_extractf128_si256(v0, 0); 
    __m128i v0l = _mm256_extractf128_si256(v0, 1); 
    __m128i v1h = _mm256_extractf128_si256(v1, 0); 
    __m128i vouth = _mm_alignr_epi8(v0l, v0h, n); 
    __m128i voutl = _mm_alignr_epi8(v1h, v0l, n); 
    __m256i vout = _mm256_set_m128i(voutl, vouth); 
    return vout; 
    } 
    else 
    { 
    __m128i v0h = _mm256_extractf128_si256(v0, 1); 
    __m128i v0l = _mm256_extractf128_si256(v1, 0); 
    __m128i v1h = _mm256_extractf128_si256(v1, 1); 
    __m128i vouth = _mm_alignr_epi8(v0l, v0h, n - 16); 
    __m128i voutl = _mm_alignr_epi8(v1h, v0l, n - 16); 
    __m256i vout = _mm256_set_m128i(voutl, vouth); 
    return vout; 
    } 
} 

co moim zdaniem jest prawie identyczne do rozwiązania oprócz tego, że również obsługuje przesunięcia> = 16 bajtów.

+0

Tak, to to samo rozwiązanie. ale jeśli jest to jedyny sposób, to wygląda na to, że projektanci instrukcji AVX2 mają duże niedopatrzenie. – eladidan

+0

Nie mogłem tego skompilować ... Dostaję błąd kompilacji: "błąd katastroficzny: parametr wewnętrzny musi być wartością natychmiastową" w następującym wierszu: "__m128i vouth = _mm_alignr_epi8 (v0l, v0h, n);". Supposidely, ponieważ n nie jest immidiate. Jak mogłeś to ominąć? Używam kompilatora Intel C++ – eladidan

+0

Działa to dla mnie, o ile n jest stałą czasu kompilacji - używam również kompilatora Intel ICC, ale kompiluję jako C zamiast C++, jeśli to robi jakąkolwiek różnicę, a także działa dla mnie z gcc. –

4

Do czego służy palignr? Jeśli jest to tylko kwestia niewspółosiowości danych, po prostu użyj niewyrównanych obciążeń; są one na ogół "wystarczająco szybkie" na nowoczesnych architekturach Intel μ (i zaoszczędzisz wiele kodu).

Jeśli zachodzi potrzeba zachowania podobnego do tego z innej przyczyny, można po prostu skorzystać z niezaliczonej obsługi obciążeń, aby wykonać to w sposób wolny od gałęzi. Jeśli nie jesteś całkowicie związany z magazynem, jest to prawdopodobnie preferowany idiom.

static inline __m256i _mm256_alignr_epi8(const __m256i v0, const __m256i v1, const int n) 
{ 
    // Do whatever your compiler needs to make this buffer 64-byte aligned. 
    // You want to avoid the possibility of a page-boundary crossing load. 
    char buffer[64]; 

    // Two aligned stores to fill the buffer. 
    _mm256_store_si256((__m256i *)&buffer[0], v0); 
    _mm256_store_si256((__m256i *)&buffer[32], v1); 

    // Misaligned load to get the data we want. 
    return _mm256_loadu_si256((__m256i *)&buffer[n]); 
} 

Jeśli możesz dostarczyć więcej informacji o jak dokładnie używasz palignr, mogę chyba być bardziej pomocny.

+0

Opóźnienie nie będzie zbyt dobre, ponieważ obciążenie będzie miało dodatkowe ~ 10 cykli opóźnienia ze stanowiska przechowania na procesorach Intela. IDK, jeśli stragany przechodzące do sklepu są problemem z przepustowością. Mogą nie być. –

+1

@PeterCordes: Nie ma zagrożenia przepustowości, tylko opóźnienie. Podejście przedstawione tutaj ma sens w sytuacjach, gdy sklep można wyciągnąć, aby ukryć opóźnienie, lub przechowywane dane można ponownie wykorzystać do wyodrębnienia szeregu różnych dopasowań. Oczywiście mamy dwa tasowania w AVX-512, które zwykle są lepszym rozwiązaniem. –

+0

Dobra racja, jest to doskonałe do generowania różnych okien na te same dwa wektory. Jest to również dobre dla liczby zmian w zmiennej runtime. –

Powiązane problemy