2012-03-20 9 views
10

Ho un vettore __m256d pieno di quattro valori a virgola mobile a 64 bit.
Ho bisogno di trovare il massimo orizzontale degli elementi del vettore e di memorizzare il risultato in un valore scalare a doppia precisione;Come trovare il massimo orizzontale in un vettore AVX a 256 bit

I miei tentativi hanno finito per utilizzare molto il rimescolamento degli elementi vettoriali, rendendo il codice non molto elegante né efficiente. Inoltre, ho trovato impossibile rimanere solo nel dominio AVX. A un certo punto ho dovuto usare le istruzioni SSE a 128 bit per estrarre il valore finale a 64 bit. Tuttavia, mi piacerebbe essere smentito in quest'ultima affermazione.

Quindi la soluzione ideale sarà:
1) utilizzare solo le istruzioni AVX.
2) ridurre al minimo il numero di istruzioni. (Spero di non più di 3-4 istruzioni)

Detto questo, qualsiasi soluzione elegante/efficiente sarà accettata, anche se non aderisce alle linee guida di cui sopra.

Grazie per qualsiasi aiuto.

-Luigi

+1

Questa è una domanda difficile ... stai facendo questo con solo 1 vettore? O hai molti vettori per i quali hai bisogno di trovare il massimo? È possibile (abbastanza) efficiente fare 4 di questi in parallelo con una trasposizione vettoriale 4 x 4 ... – Mysticial

+0

@Mysticial: Beh ... Ho a che fare con molti vettori. Tuttavia, la semplicità dell'elaborazione non giustifica due operazioni di trasposizione 4x4 per ogni iterazione. Quindi sto elaborando tutto "orizzontalmente" senza trasposizione. Sto ottenendo una grande accelerazione in questo modo, vicino a 4x, perché sto evitando il sovraccarico di trasposizione. Tutto è in un circuito chiuso manualmente srotolato 4 volte.Tuttavia, quando il ciclo è finito, mi rimane un ultimo vettore AVX. Devo trovare il massimo dei suoi quattro elementi per archiviare il risultato nel mio valore scalare a doppia precisione. Quindi la mia domanda ... –

+0

Se non è nel "circuito chiuso", è anche critica delle prestazioni? – Mysticial

risposta

12

Non penso che tu possa fare molto meglio di 4 istruzioni: 2 shuffles e 2 confronti.

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

modifica banale per funzionare solo con vettori a 256 bit:

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

(non testato)

+0

Sì, d'accordo ... Buona soluzione. Grazie. –

2

Il modo generale di fare questo per un vettore v1 = [A, B, C, D] è

  1. Permute v1-v2 = [C, D, A, B] (0a swap e 2 ° elementi, e il 1 ° e 3 ° quelli)
  2. Prendere il max ; Ad esempio v3 = max(v1,v2). Ora hai [max(A,C), max(B,D), max(A,C), max(B,D)]
  3. Permuta v3 a v4, scambiando gli elementi 0 ° e 1 ° e 2 ° e 3 °.
  4. Riprendere il massimo, ovvero v5 = max(v3,v4). Ora v5 contiene il massimo orizzontale in tutti i suoi componenti.

particolare per AVX, le permutazioni può essere fatto con _mm256_permute_pd ei massimi può essere fatto con _mm256_max_pd. Non ho le maschere di permutazione esatte a portata di mano, ma dovrebbero essere abbastanza semplici da capire.

Spero che questo aiuti.

+0

Mi piace particolarmente il tuo soluzione, perché finora è l'unico che utilizza esclusivamente le istruzioni AVX, senza mai lasciare il dominio a 256 bit. Grazie. –

+0

scusa, ho parlato troppo presto ... Non puoi farlo con AVX. La maggior parte delle operazioni AVX non supera il limite di 128 bit. Quindi in questo caso non è possibile scambiare gli elementi 0 ° e 2 ° e 1 ° e 3 °. L'operazione permutazione AVX consente solo di scambiare gli elementi 0 ° e 1 ° o 2 ° e 3 °. –

+0

@LuigiCastelli: la mia soluzione può essere scritta in modo da non lasciare mai il dominio a 256 bit, se lo si desidera. Sostituisci '_mm256_extractf128_pd' di' _mm256_permute2f128_pd (x, x, 1) ',' __m128d' di '__m256d', e' _mm _... 'di' _mm256 _... ',' _mm_permute_pd (m1, 1) 'di' _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

Ci vorrà un passo in più per i vettori float, ma la memorizzazione su un array e il confronto scalare non è uno dei passaggi. Devi comunque iniziare con un 'extractf128'/128bit' maxps'. Fare cose in-lane prima non è migliore con le CPU Intel, e decisamente peggio con le CPU AMD in cui le operazioni con AVX a 256 bit sono due volte più costose delle operazioni con AVX a 128 bit. Ad ogni modo, un negozio da 256b e poi due carichi -> un confronto scalare è semplicemente sciocco, e più lento di un 'extractf128'. –