2012-03-20 12 views
10

Mam wektor __m256d z czterema 64-bitowymi wartościami zmiennoprzecinkowymi.
Potrzebuję znaleźć poziomą wartość maksymalną elementów wektora i zapisać wynik w podwójnej precyzji wartości skalarnej;Jak znaleźć poziomą wartość maksymalną w 256-bitowym wektorze AVX

Wszystkie moje próby zakończyły się użyciem wielu tasowań elementów wektorowych, dzięki czemu kod nie był zbyt elegancki ani wydajny. Ponadto nie mogłem pozostać tylko w domenie AVX. W pewnym momencie musiałem użyć 128-bitowych instrukcji SSE, aby wyodrębnić ostateczną wartość 64-bitową. Chciałbym jednak udowodnić, że nie mam racji w tym ostatnim oświadczeniu.

Idealne rozwiązanie:
1) Używaj tylko instrukcji AVX.
2) zminimalizować liczbę instrukcji. (Mam nadzieję, że nie więcej niż 3-4 instrukcje)

Po tym wszystkim, wszelkie eleganckie/wydajne rozwiązania będą akceptowane, nawet jeśli nie będą zgodne z powyższymi wytycznymi.

Dzięki za pomoc.

-Luigi

+1

To trudne pytanie ... Czy robisz to tylko 1 wektora? Czy masz wiele wektorów, dla których musisz znaleźć maksimum? Możesz (dość) wydajnie zrobić 4 z nich równolegle z transpozycją wektora 4 x 4 ... – Mysticial

+0

@Mysticial: Cóż ... Mam do czynienia z wieloma wektorami. Jednak prostota przetwarzania nie usprawiedliwia dwóch operacji transpozycji 4x4 dla każdej iteracji. Przetwarzam wszystko "w poziomie" bez transpozycji. W ten sposób uzyskuję duże przyspieszenie, zbliżone do 4x, ponieważ unikam nakładu transpozycji. Wszystko jest w ciasnej pętli ręcznie rozwijane 4 razy.Jednak po zakończeniu pętli pozostawiam jeden ostatni wektor AVX. Muszę znaleźć największy z czterech elementów, aby zapisać wynik z powrotem w mojej podwójnej precyzji wartości skalarnej. Stąd moje pytanie ... –

+0

Jeśli nie jest w "ciasnej pętli", czy jest to nawet krytyczne? – Mysticial

Odpowiedz

12

Nie sądzę, że można zrobić znacznie lepiej niż 4 instrukcje: 2 tasowania i 2 porównania.

__m256d x = ...; // input 

__m128d y = _mm256_extractf128_pd(x, 1); // extract x[2], and x[3] 
__m128d m1 = _mm_max_pd(x, y); // m1[0] = max(x[0], x[2]), m1[1] = max(x[1], x[3]) 
__m128d m2 = _mm_permute_pd(m1, 1); // set m2[0] = m1[1], m2[1] = m1[0] 
__m128d m = _mm_max_pd(m1, m2); // both m[0] and m[1] contain the horizontal max(x[0], x[1], x[2], x[3]) 

Trivial modyfikacje działają tylko z 256-bitowych wektorów:

__m256d x = ...; // input 

__m256d y = _mm256_permute2f128_pd(x, x, 1); // permute 128-bit values 
__m256d m1 = _mm256_max_pd(x, y); // m1[0] = max(x[0], x[2]), m1[1] = max(x[1], x[3]), etc. 
__m256d m2 = _mm256_permute_pd(m1, 5); // set m2[0] = m1[1], m2[1] = m1[0], etc. 
__m256d m = _mm256_max_pd(m1, m2); // all m[0] ... m[3] contain the horizontal max(x[0], x[1], x[2], x[3]) 

(niesprawdzone)

+0

Tak, uzgodniono ... Dobre rozwiązanie. Dzięki. –

2

Ogólny sposób to zrobić za pomocą wektora v1 = [A, B, C, D] jest

  1. permutuj v1 do v2 = [C, D, A, B] (0TH wymiany i 2. elementów oraz 1 i 3 z nich)
  2. Weź max ; tj. v3 = max(v1,v2). Masz teraz [max(A,C), max(B,D), max(A,C), max(B,D)]
  3. Permute v3 do v4, zamieniając elementy 0 i 1 oraz 2 i 3.
  4. Zrób ponownie maks., Czyli v5 = max(v3,v4). Teraz v5 zawiera poziome maksimum we wszystkich jego składnikach.

szczególności dla AVX, permutacje mogą być wykonane z _mm256_permute_pd i maksimów mogą być wykonane z _mm256_max_pd. Nie mam przy sobie dokładnych masek permutowych, ale powinny być one dość proste do wymyślenia.

Nadzieję, że pomaga.

+0

Szczególnie podoba mi się twój rozwiązanie, ponieważ do tej pory jest jedynym, który używa wyłącznie instrukcji AVX, nigdy nie opuszczając 256-bitowej domeny. Dzięki. –

+0

Przepraszam, mówiłem za wcześnie ... Nie możesz tego zrobić z AVX. Większość operacji AVX nie przekracza granicy 128-bitowej. W tym przypadku nie można zamienić elementów 0 i 2 oraz 1. i 3. miejsca. Operacja permutacji AVX pozwala tylko na zamianę elementów 0 i 1 lub 2 i 3. –

+0

@LuigiCastelli: moje rozwiązanie można zapisać tak, aby nigdy nie opuszczać 256-bitowej domeny, jeśli chcesz. Zastąp '_mm256_extractf128_pd' przez' _mm256_permute2f128_pd (x, x, 1) ',' __m128d' przez '__m256d' i' _mm _... 'przez' _mm256 _... ',' _mm_permute_pd (m1, 1) 'przy użyciu polecenia' _mm256_permute_pd (m1, 5) ". –

-1
//Use the code to find the horizontal maximum 
__m256 v1 = initial_vector;//example v1=[1 2 3 4 5 6 7 8] 
__m256 v2 = _mm256_permute_ps(v1,(int)147);//147 is control code for rotate left by upper 4 elements and lower 4 elements separately v2=[2 3 4 1 6 7 8 5] 
__m256 v3 = _mm256_max_ps(v1,v2);//v3=[2 3 4 4 6 7 8 8] 
__m256 v4 = _mm256_permute_ps(v3,(int)147);//v4=[3 4 4 2 7 8 8 6] 
__m256 v5 = _mm256_max_ps(v3,v4);//v5=[3 4 4 4 7 8 8 8] 
__m256 v6 = _mm256_permute_ps(v5,(int)147);//v6=[4 4 4 3 8 8 8 7] 
__m256 v7 = _mm256_max_ps(v5,v6);//contains max of upper four elements and lower 4 elements. v7=[4 4 4 4 8 8 8 8] 

//to get max of this horizontal array. Note that either upper or lower can contain the maximum 
float ALIGN max_array[8]; 
float horizontal_max; 
_mm256_store_ps(max_array, v7); 
if(max_array[0] > max_array[7]) 
{ 
    horizontal_max = max_array[0]; 
} 
else 
{ 
    horizontal_max = max_array[7]; 
} 
+1

Wykonanie jednego dodatkowego kroku dla wektorów swobodnych, ale zapisanie do tablicy i wykonanie porównania skalarnego nie jest jednym z kroków. Nadal chcesz zacząć od 'extractf128'/128bit' maxps'. Robienie rzeczy w pierwszej linii nie jest lepsze na procesorach Intela i zdecydowanie gorzej na procesorach AMD, gdzie 256k AVX op jest dwa razy droższe niż 128b AVX ops. Tak czy inaczej, magazyn 256b, a następnie dwa ładunki -> porównanie skalarne jest po prostu głupie i wolniejsze niż "extractf128". –

Powiązane problemy