2014-07-06 12 views
8

Mam następujący problem:permutację bajtów wewnątrz SSE __m128i zarejestrować

W __m128i rejestr istnieje 16 wartości 8bit w następującej kolejności:

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

Co chciałbym osiągnąć to skutecznie losowe bajty, aby to Kolejność:

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

jest rzeczywiście analogowo 4x4 macierzy przeniesienia, ale działa na 8-bitowym elementu wewnątrz jeden R egister.

Czy możesz wskazać mi, jaki rodzaj SSE (najlepiej < = SSE2) instrukcje są odpowiednie do realizacji tego?

+2

Cóż z ssse3 tam 'pshufb', inaczej to będzie trochę niechlujny – harold

+0

Dzięki za zwrócenie mi na pshufb! Myślę, że mogę się stamtąd ruszyć :) – born49

+0

Oprócz SSSE3 'PSHUFB', który jest rodzajem Intel wyłączności na starszych procesorów, patrzysz na niechlujny sekwencji instrukcji SSE2' PUNPCK (L/H) BW', jak @harold wskazał. –

Odpowiedz

5

Naprawdę będzie chciał iść ssse3 za to, że o wiele bardziej czysty niż próbuje przejść < = SSE2

Twój kod będzie wyglądać następująco:

#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); 

Jeśli naprawdę chcesz SSE2 to wystarczy:
(zakładając, że mam poprawnie interpretując swoją początkową kolejność)

__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] 

lub więcej 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

Wielkie dzięki za wyjaśnienie! Będę używał SSE3 jako ścieżki głównej i zastępowania wersji SSE2 na platformach bez SSE3. – born49

+0

@ user3809354: Nie ma problemu! Pamiętaj, że będziesz chciał wywołać CPUID raz podczas uruchamiania i przechowywać wszystko, czego potrzebujesz, aby ustalić, która wersja kodu ma zostać uruchomiona. [MSDN] (http://msdn.microsoft.com/en-us/library/y0dh78ez (vs.80) .aspx) jest całkiem dobrym punktem wyjścia do zobaczenia, co tam jest. Możesz nawigować w górę drzewa, aby zobaczyć wewnętrzne elementy SSSE3 i SSE4. Stwierdzone przeze mnie rzeczy mogłyby naprawdę wykorzystywać przykłady (np. Znaczenie wartości maski losowej), ale właśnie to jest SO. :) – Apriori

+0

@ user3809354: btw, zdałem sobie sprawę, że możesz zapisać kilka instrukcji w wersji SSE2, wywołując _mm_srli_epi16 zamiast _mm_srli_si128, ponieważ przesunie się w zera dla każdego 16-bitowego komponentu. Pozwala to uniknąć późniejszej maski, ponieważ zera muszą znajdować się w górnym bajcie każdego 16-bitowego komponentu przed wywołaniem pakietu. Jest tak, ponieważ nie ma wersji pakietu, która obcina (tj. _mm_packs_epi16 i _mm_packus_epi16 wykonują odpowiednio podpisane i unsigned saturate), więc aby wyrzucić górny bajt, musi zawierać zera. Zaktualizowałem kod odpowiedzi, aby to odzwierciedlić. – Apriori

Powiązane problemy