2015-05-19 10 views
6

(Jestem zainteresowany tylko w 1. 3 komponentów)Najlepszy sposób obliczyć max maskę SSE var

Na przykład:
[ 1 2 3 ? ] powinna produkować [ 0 0 -1 ? ]

Ponadto, ważne jest, aby mieć tylko jeden " nieco”ustawić tak, aby:
[ 1 2 2 ? ] nie powinien produkować [ 0 -1 -1 ? ]
ale raczej
[ 0 -1 0 ? ] lub [ 0 0 -1 ? ] (nie ma znaczenia, który z nich)

Późniejsza (złe) rozwiązanie jest możliwe, na przykład poprzez ekstrakcję poziome max i porównanie z oryginałem:

__m128 abcd; // input 
__m128 ccac   = _mm_shuffle_ps(abcd, abcd, 0x8A); 
__m128 abcd_ccac  = _mm_max_ps(abcd, ccac); 
__m128 babb   = _mm_shuffle_ps(abcd, abcd, 0x51); 
__m128 abcd_ccac_babb = _mm_max_ps(abcd_ccac, babb); 
__m128 mask   = _mm_cmpeq_ps(abcd, abcd_ccac_babb); 

Być może niektórzy bitowe operacje, aby pozbyć się duplikatów zestaw bitów?

Aktualizacja:

Kontynuacja, zrobiłem kolejny (złego) rozwiązanie.
Kluczem jest porównanie każdego komponentu z innym, unikanie równań równości (nie mając w innym miejscu a >= b i b >= a).

a > b & a >= c
b > c & b >= a
c > a & c >= b

otrzymując:

([ a b c ? ] > [ b c a ? ]) & ([ a b c ? ] >= [ c a b ? ])

i w postaci kodu:

__m128 abcd; // input 
__m128 bcad = _mm_shuffle_ps(abcd, abcd, 0xC9); 
__m128 gt = _mm_cmpgt_ps(abcd, bcad); 
__m128 cabd = _mm_shuffle_ps(abcd, abcd, 0xD2); 
__m128 ge = _mm_cmpge_ps(abcd, cabd); 
__m128 mask = _mm_and_ps(gt, ge); 

to nie działa w przypadku [ x x x ? ] (produkuje [ 0 0 0 ? ]).

Zbliżanie się :-)

Jakieś pomysły?

Aktualizacja:

Obecnie używam następujące rozwiązanie:

__m128 abcd; // input 
__m128 bcad = _mm_shuffle_ps(abcd, abcd, 0xC9); 
__m128 gt = _mm_cmpgt_ps(abcd, bcad); 
__m128 cabd = _mm_shuffle_ps(abcd, abcd, 0xD2); 
__m128 ge = _mm_cmpge_ps(abcd, cabd); 
__m128 and = _mm_and_ps(gt, ge); 
__m128i bits = _mm_setr_epi32(_mm_movemask_ps(and), -1, -1, -1); 
__m128i dirt = _mm_cmpeq_epi32(bits, _mm_setzero_si128()); 
__m128i mask = _mm_or_si128(dirt, _mm_castps_si128(and)); 
+2

Nie widzę żadnego wydajnego ani eleganckiego sposobu robienia tego w tej chwili - wszystko, co mogę wymyślić do tej pory, to wyodrębnienie 4 bitów MS z 'maski' przy użyciu' _mm_movemask_ps', a następnie przetworzenie tego, aby uzyskać unikalny wartość jednobitowa, którą można następnie przekonwertować z powrotem na wektor maski. –

+0

Czy musisz zrobić to sekwencyjnie, czy możesz to zrobić niezależnie, na przykład, jeśli masz tablicę 2D podobną do {{1,2,3}, {4,5,6}, ...} czy możesz znaleźć maksimum dla {1,2,3} i {4,5,6} niezależnie? Jeśli możesz to zrobić niezależnie dla różnych zestawów liczb, to będzie to bardziej efektywne dzięki SIMD. –

+1

@PaulR '_mm_movemask_ps' jako podejście do operacji bitowych jest bardzo mile widziane. –

Odpowiedz

2

Nie testowałem tego, ale wierzę, to będzie cię -1 jedynie pierwszą (zarówno najwyższym zamówienie) wystąpienie maksymalnej wartości:

__m128 abcd; // input 
__m128 ccac = _mm_shuffle_ps(abcd,abcd,0x8A); 
__m128 abcd_ccac = _mm_max_ps(abcd,ccac); 
__m128 babb = _mm_shuffle_ps(abcd,abcd,0x51); 
__m128 abcd_ccac_babb = _mm_max_ps(abcd_ccac,babb); 
__m128 mask = _mm_cmpeq_ps(abcd,abcd_ccac_babb); 

// set the '?' position in mask to zero 
mask = _mm_blend_ps(mask,_mm_setzero_ps(),0x08); 
// shift mask left 32 bits shifting in zeros 
__m128 maskSrl32 = _mm_shuffle_ps(mask,mask,_MM_SHUFFLE(3,1,0,3)); 
// shift mask left 64 bits shifting in zeros 
__m128 maskSrl64 = _mm_shuffle_ps(mask,mask,_MM_SHUFFLE(3,0,3,3)); 
// andnot the shifted masks with mask 
// in doing so, the higher order set bits will suppress any set bits which follow 
mask = _mm_andnot_ps(maskSrl32,mask); 
mask = _mm_andnot_ps(maskSrl64,mask); 
// select -1 using the final mask 
__m128 result = _mm_and_ps(mask,_mm_set1_ps(-1.0f)); 

Odwróć kierunek przesuwania, aby zamiast tego uzyskać -1 w najniższej pozycji maksymalnego położenia.

+0

To nie jest typowy przykład: '[4, 3, 2 x]' (tworzy '[0 0 0 x]'). (co to za cel??) –

+1

@GolanRoss Naprawiłem podprogram i przetestowałem go. To powinno teraz działać dobrze (początkowo miałem uporządkowaną sekwencję elementów wektorowych w mojej głowie). "?" pozycja jest elementem, który powiedziałeś, że nie jesteś zainteresowany. To musi być wyczyszczone, aby ułatwić przesuwanie elementów. – chili

+1

Interesujący wybór 'blend_ps' do selektywnego zerowania, zamiast' i_ps' z maską. Jest bardziej wydajny niż ja: single uop i może działać na 2 lub 3 portach Intel SnB/Haswell (odpowiednio). Możesz jednak zerować za darmo za pomocą '_mm_bslli_si128 (src, byte_count)' zamiast 'shuffle_ps' (Musisz rzucić na' __m128i' .Nie sądzę, że istnieje dodatkowe opóźnienie (opóźnienie bypassu) do przekazania wynik z 'cmpeq_ps' na liczbę całkowitą shuffle zamiast FP shuffle. (' pslldq' działa na jednostce losowej, tak samo jak 'shufps'). –