2012-04-02 11 views
12

Wewnętrzna _mm_slli_si128 zrobi logiczne przesunięcie w lewo od rejestru 128-bitowego, ale jest ograniczona do natychmiastowych wartości przesunięcia i przesuwa się o bajty, a nie bity.Poszukuje operacji 128-bitowego przesunięcia dla nie-natychmiastowej zmiany wartości

Mogę użyć wewnętrznego, takiego jak _mm_sll_epi64 lub _mm_sll_epi32, aby przesunąć w lewo zestaw wartości z rejestru __m128i, ale te nie przenoszą bitów "przepełnienia".

do przesunięcia przez N bitów wyobrazić, że mógłbym zrobić coś takiego:

  • _mm_sll_epi64
  • _mm_srr_epi64 (dla bitów chcę nosić: przenieść je na niskim kolejności)
  • shuffle, wynik srr:
  • lub razem.

(ale prawdopodobnie również musi zawierać sprawdzanie N w stosunku do 64).

Czy istnieje lepszy sposób?

+1

Nie sądzę, że istnieje lepszy sposób. Napisałem odpowiedź na ostatni duplikat tego pytania: http://stackoverflow.com/q/34478328/224132. W przypadku liczenia stałych w czasie kompilacji, zmienia się w 4 insony lub 2 insns z count> = 64. Przy zmiennej liczbie, rozgałęzia się i musi "przemieścić" liczbę i 64-count z liczb całkowitych do rejestrów wektorowych. "__uint128_t" w tym przypadku jest lepsze, jeśli dane są już w rejestrach całkowitych. –

Odpowiedz

4

Nie jest to idealne rozwiązanie, ale jeśli chcesz obrócić lub przesunąć rejestr SSE o liczbę bitów, która jest wielokrotnością liczby 8, pomocna może być instrukcja PSHUFB (i wewnętrzna nazwa _mm_shuffle_epi8()). Wymaga drugiego rejestru SSE jako danych wejściowych; każdy bajt w rejestrze zawiera wartość, która jest używana do indeksowania bajtów w pierwszym rejestrze wejściowym.

+4

Sądzę, że PO wyraźnie stwierdził, że chce on być drobnoziarnisty, a nie ograniczony do bezpośrednich. '_mm_shuffle_epi8()' ma zarówno granice bajtów, jak i natychmiastowe. – Mysticial

+4

Wiem, że chciał nieco rozdrobnienia, stąd pierwsza klauzula w mojej odpowiedzi. Również '_mm_shuffle_epi8()' nie wymaga natychmiastowego; drugi argument to wartość '__m128i'. [Zobacz dokumentację tutaj] (http://msdn.microsoft.com/en-us/library/bb531427.aspx). –

+1

Należy zauważyć, że ta funkcja wymaga obsługi SSSE3, co może być niewystarczające, jeśli chcesz działać na starszych komputerach. –

4

Ten problem pojawił się jako problem dodatkowy w poście na blogu pod adresem unusual C preprocessor uses. Dla 127 różnych przesunięć przesunięcia istnieją cztery różne optymalne sekwencje instrukcji SSE2 dla przesunięcia bitowego. Preprocesor sprawia, że ​​rozsądne jest skonstruowanie funkcji zmiany, która odpowiada 129-stykowej instrukcji przełączania. Ułóż tutaj kod źródłowy; Nie mam pojęcia, jak opublikować kod bezpośrednio tutaj. Sprawdź wpis na blogu, aby dowiedzieć się, co się dzieje.

#include <emmintrin.h> 

typedef __m128i XMM; 
#define xmbshl(x,n) _mm_slli_si128(x,n) // xm <<= 8*n -- BYTE shift left 
#define xmbshr(x,n) _mm_srli_si128(x,n) // xm >>= 8*n -- BYTE shift right 
#define xmshl64(x,n) _mm_slli_epi64(x,n) // xm.hi <<= n, xm.lo <<= n 
#define xmshr64(x,n) _mm_srli_epi64(x,n) // xm.hi >>= n, xm.lo >>= n 
#define xmand(a,b) _mm_and_si128(a,b) 
#define xmor(a,b) _mm_or_si128(a,b) 
#define xmxor(a,b) _mm_xor_si128(a,b) 
#define xmzero  _mm_setzero_si128() 

XMM xm_shl(XMM x, unsigned nbits) 
{ 
    // These macros generate (1,2,5,6) SSE2 instructions, respectively: 
    #define F1(n) case 8*(n): x = xmbshl(x, n); break; 
    #define F2(n) case n: x = xmshl64(xmbshl(x, (n)>>3), (n)&15); break; 
    #define F5(n) case n: x = xmor(xmshl64(x, n), xmshr64(xmbshl(x, 8), 64-(n))); break; 
    #define F6(n) case n: x = xmor(xmshl64(xmbshl(x, (n)>>3), (n)&15),\ 
            xmshr64(xmbshl(x, 8+((n)>>3)), 64-((n)&155))); break; 
    // These macros expand to 7 or 49 cases each: 
    #define DO_7(f,x) f((x)+1) f((x)+2) f((x)+3) f((x)+4) f((x)+5) f((x)+6) f((x)+7) 
    #define DO_7x7(f,y) DO_7(f,(y)+1*8) DO_7(f,(y)+2*8) DO_7(f,(y)+3*8) DO_7(f,(y)+4*8) \ 
             DO_7(f,(y)+5*8) DO_7(f,(y)+6*8) DO_7(f,(y)+7*8) 
    switch (nbits) { 
    case 0: break; 
    DO_7(F5, 0) // 1..7 
    DO_7(F1, 0) // 8,16,..56 
    DO_7(F1, 7) // 64,72,..120 
    DO_7x7(F6, 0) // 9..15 17..23 ... 57..63 i.e. [9..63]\[16,24,..,56] 
    DO_7x7(F2,56) // 65..71 73..79 ... 121..127 i.e. [65..127]\[64,72,..,120] 
    default: x = xmzero; 
    } 
    return x; 
} 

xm_shr wynosi powyżej ale zamiana "SHL" i "SHR" wszędzie w F [1256] makr. HTH.

+2

W rzeczywistości powyższy kod nie działa dla około połowy wartości przesunięcia. Przetestowałem to na standardowym przesunięciu 128-bitowych liczb całkowitych (gcc obsługuje __uint128_t), a wyniki są znacząco różne.Na przykład, wszystkie zmiany powyżej 120 tylko zera wszystkie bity. – seba

+1

Aby uzyskać stałą liczbę zmian w czasie kompilacji, nigdy nie potrzebujesz więcej niż 4 instrukcji (lub 5 bez AVX: dodatkowe 'movdqa'). Dla count <64, przesunięcie bajtów pozostawione przez 64b, a następnie przesunięcie bitowe, które przenosi prawo na 64-krotność. 'OR' przeniesienie z' psllq xmm0, 64'. Napisałem go z 'if', a ono ładnie kompiluje się na podstawie stałej czasu kompilacji: http://goo.gl/O14GhI. Zobacz http://stackoverflow.com/a/34482688/224132 –

+0

Aby naprawić kod, po prostu zamień każde & 15 lub & 155 wyrażenie przez & 7. To powiedziawszy, ten kod jest bardzo powolny (czy wiesz o rozgałęzieniach ?!), a sugestia Petera Cordesa wygląda o wiele bardziej obiecująco. –

Powiązane problemy