2015-06-19 23 views
8

ja Wektoryzacja kawałek kodu i w pewnym momencie mam następującą konfigurację:Multiply-odejmować w SSE

register m128 a = { 99,99,99,99,99,99,99,99 } 
register m128 b = { 100,50,119,30,99,40,50,20 } 

Jestem obecnie pakowania short S w tych rejestrach, dlatego mam 8 wartości na zarejestrować. Chciałbym odjąć pierwszy element w postaci b z odpowiednią wartością w a, jeśli wartość 1 to b jest większa niż lub równa wartości w a (w tym przypadku a jest wypełniona stałą 99). W tym celu, najpierw wykorzystać większy niż lub równy pracy pomiędzy b i a, co daje, w tym przykładowo:

register m128 c = { 1,0,1,0,1,0,0,0 } 

do zakończenia operacji, że nie mogło być użyciu wielowarstwowej-i-Odejmij , czyli do przechowywania w b operacji b -= a*c. Wynik byłby następujący:

b = { 1,50,20,30,0,40,50,20 } 

Czy istnieje jakakolwiek operacja, która robi coś takiego? To, co znalazłem, było połączonymi operacjami dla Haswella, ale obecnie pracuję nad Sandy-Bridge. Ponadto, jeśli ktoś ma lepszy pomysł, aby to zrobić, proszę dać mi znać (np mogłem zrobić logiczną Odejmij. Jeśli 1 w c potem odjąć, nic poza tym

+0

Właśnie w celu wyjaśnienia, powinna być operacja 'b - = a * C', aby wykonać odpowiednie odejmowanie? – Alejandro

+0

Prawidłowo, @Alejandro – a3mlord

Odpowiedz

0

Można skopiować b do c, odejmowanie a od c, wykonać arytmetyczne przesunięcie w prawo o 15 pozycji w 16-bitowych wartości, uzupełniają wartość c, maska ​​c z a i wreszcie odjąć c z b.

nie jestem zaznajomiony z składni intrinsics, ale kroki to:

register m128 c = b; 
c -= a; 
c >>= 15; 
c = ~c; 
c &= a; 
b -= c; 

tutaj jest alternatywą przy mniejszej liczbie kroków:

register m128 c = compare_ge(b, a); 
c = -c; 
c &= a; 
b -= c; 
3

można zasadniczo chcemy wersję SSE tego kodu, prawda?

if (b >= a) 
    t = b-a 
else 
    t = b 
b = t 

Ponieważ chcemy uniknąć warunkowe dla wersji SSE więc możemy pozbyć się kontroli przepływu tak (zauważ, że maska ​​jest odwrócony):

uint16_t mask = (b>=a)-1 
uint16_t tmp = b-a; 
uint16_t d = (b & mask) | (tmp & ~mask) 
b = d 

mam zaznaczone _mm_cmpgt_epi16 Wewnętrzna i ma ładną właściwość polegającą na tym, że zwraca wartość 0x0000 dla wartości false lub 0xFFFF dla wartości true, zamiast dla pojedynczego bitu 0 lub 1 (eliminując w ten sposób konieczność pierwszego odejmowania). Dlatego nasza wersja SSE może wyglądać tak.

__m128i mask = _mm_cmpgt_epi16 (b, a) 
__m128i tmp = _mm_sub_epi16 (b, a) 
__m128 d = _mm_or_ps (_mm_and_ps (mask, tmp), _mm_andnot_ps (mask, b)) 

EDIT: Harold wspomniał o wiele mniej skomplikowany odpowiedź. Powyższe rozwiązanie może być pomocne, jeśli musisz zmodyfikować część część część , jeśli/else.

uint16_t mask = ~((b>=a)-1) 
uint16_t tmp = a & mask 
b = b - tmp 

kod SSE będzie

__m128i mask = _mm_cmpgt_epi16 (b, a) 
__m128i t = _mm_sub_epi16 (b, _mm_and_si128 (mask, a)) 
+0

Z wyjątkiem tego, że użyłeś 'd', gdzie mam' c', tak. Twoja operacja 'b = d' nie jest potrzebna w moim przypadku, ponieważ jestem w porządku z operacjami poza miejscem. Myślę, że nie chcesz robić 'm = (b> = a) -1' ale' m = (b> = a) ', prawda? – a3mlord

+0

Nie. Twoje 'c' to moje' m' (dla maski). Zmienna 'd' ma na celu uczynienie jej bardziej czytelną. Również 'm = (b> = a) -1' jest poprawne, ponieważ wtedy konwertujemy maskę jednobitową (0 lub 1) do maski 8-bitowej (0000000 lub 11111111), której potrzebujemy dla następnej linii. – hayesti

+0

@ a3mlord Właśnie przyjrzałem się dokładniej 'mm_cmpgt'. W rzeczywistości zwraca 0x00 lub 0xFF, więc w drugiej chwili nie będziemy potrzebować wartości -1, ale będziemy musieli zmienić kolejność operatorów i operatorów. Będę edytować mój post teraz ... – hayesti