2011-12-15 9 views
7

Nel SSE3, l'istruzione PALIGNR esegue le seguenti:_mm_alignr_epi8 (PALIGNR) equivalente in AVX2

PALIGNR concatenates the destination operand (the first operand) and the source operand (the second operand) into an intermediate composite, shifts the composite at byte granularity to the right by a constant immediate, and extracts the right-aligned result into the destination.

Sono attualmente nel mezzo di porting mio codice SSE4 di utilizzare istruzioni AVX2 e lavorando su 256bit registra, invece di 128bit. Ingenuamente, credevo che la funzione intrinseca _mm256_alignr_epi8 (VPALIGNR) eseguisse la stessa operazione di _mm_alignr_epi8 solo su registri a 256 bit. Purtroppo però, non è esattamente il caso. Infatti, lo _mm256_alignr_epi8 considera il registro a 256 bit come 2 registri a 128 bit ed esegue 2 operazioni di "allineamento" sui due registri a 128 bit adiacenti. Effettivamente eseguendo la stessa operazione di _mm_alignr_epi8 ma su 2 registri contemporaneamente. E 'più chiaramente illustrato qui: _mm256_alignr_epi8

Attualmente la mia soluzione è quella di continuare a utilizzare _mm_alignr_epi8 suddividendo il YMM (256bit) registra in due XMM (128 bit) registri (alti e bassi), in questo modo:

__m128i xmm_ymm1_hi = _mm256_extractf128_si256(ymm1, 0); 
__m128i xmm_ymm1_lo = _mm256_extractf128_si256(ymm1, 1); 
__m128i xmm_ymm2_hi = _mm256_extractf128_si256(ymm2, 0); 
__m128i xmm_ymm_aligned_lo = _mm_alignr_epi8(xmm_ymm1_lo, xmm_ymm1_hi, 1); 
__m128i xmm_ymm_aligned_hi = _mm_alignr_epi8(xmm_ymm2_hi, xmm_ymm1_lo, 1); 
__m256i xmm_ymm_aligned = _mm256_set_m128i(xmm_ymm_aligned_lo, xmm_ymm_aligned_hi); 

Funziona, ma ci deve essere un modo migliore, giusto? Esiste forse un'istruzione AVX2 "generale" che dovrebbe essere utilizzata per ottenere lo stesso risultato?

risposta

2

L'unica soluzione sono stato in grado di elaborare per questo è:

static inline __m256i _mm256_alignr_epi8(const __m256i v0, const __m256i v1, const int n) 
{ 
    if (n < 16) 
    { 
    __m128i v0h = _mm256_extractf128_si256(v0, 0); 
    __m128i v0l = _mm256_extractf128_si256(v0, 1); 
    __m128i v1h = _mm256_extractf128_si256(v1, 0); 
    __m128i vouth = _mm_alignr_epi8(v0l, v0h, n); 
    __m128i voutl = _mm_alignr_epi8(v1h, v0l, n); 
    __m256i vout = _mm256_set_m128i(voutl, vouth); 
    return vout; 
    } 
    else 
    { 
    __m128i v0h = _mm256_extractf128_si256(v0, 1); 
    __m128i v0l = _mm256_extractf128_si256(v1, 0); 
    __m128i v1h = _mm256_extractf128_si256(v1, 1); 
    __m128i vouth = _mm_alignr_epi8(v0l, v0h, n - 16); 
    __m128i voutl = _mm_alignr_epi8(v1h, v0l, n - 16); 
    __m256i vout = _mm256_set_m128i(voutl, vouth); 
    return vout; 
    } 
} 

che credo è praticamente identica alla soluzione tranne che gestisce anche turni di> = 16 byte.

+0

sì, è la stessa soluzione. ma se questo è l'unico modo, allora sembra una grande svista dai progettisti delle istruzioni AVX2 – eladidan

+0

Non ho potuto ottenere questo per compilare ... ottengo l'errore di compilazione: "errore catastrofico: il parametro intrinseco deve essere un valore immediato" nella riga seguente: "__m128i vouth = _mm_alignr_epi8 (v0l, v0h, n);". Supposivamente, perché n non è un immidiato. Come sei riuscito a bypassare questo? Sto usando Intel C++ Compiler – eladidan

+0

Funziona per me, purché n sia una costante in fase di compilazione - Sto usando anche il compilatore Intel ICC, ma sto compilando come C piuttosto che C++ se questo fa alcuna differenza, e anche lavora per me con gcc. –

4

Per cosa stai utilizzando palignr? Se si tratta solo di gestire il disallineamento dei dati, utilizzare semplicemente carichi disallineati; in genere sono "abbastanza veloci" sulle moderne architetture Intel μ (e consentono di risparmiare un sacco di dimensioni del codice).

Se è necessario il comportamento di palignr -like per qualche altro motivo, è sufficiente sfruttare il supporto del carico non allineato per farlo in un ambiente privo di diramazioni. A meno che tu non sia totalmente vincolato al carico, questo è probabilmente l'idioma preferito.

static inline __m256i _mm256_alignr_epi8(const __m256i v0, const __m256i v1, const int n) 
{ 
    // Do whatever your compiler needs to make this buffer 64-byte aligned. 
    // You want to avoid the possibility of a page-boundary crossing load. 
    char buffer[64]; 

    // Two aligned stores to fill the buffer. 
    _mm256_store_si256((__m256i *)&buffer[0], v0); 
    _mm256_store_si256((__m256i *)&buffer[32], v1); 

    // Misaligned load to get the data we want. 
    return _mm256_loadu_si256((__m256i *)&buffer[n]); 
} 

Se è possibile fornire ulteriori informazioni su come esattamente si sta utilizzando palignr, posso probabilmente più utile.

+0

La latenza non sarà molto buona, perché il carico avrà un extra di ~ 10 cicli di latenza da uno stallo di inoltro del negozio su CPU Intel. IDK se le bancarelle di inoltro dei negozi sono un problema di throughput, però. Potrebbero non esserlo. –

+1

@PeterCordes: non c'è pericolo di throughput, solo latenza. L'approccio abbozzato qui ha senso in situazioni in cui il negozio può essere issato per nascondere la latenza oi dati memorizzati possono essere riutilizzati per estrarre una varietà di allineamenti diversi. Ovviamente, abbiamo shuffle a due sorgenti in AVX-512, che di solito rappresentano una soluzione migliore. –

+0

Oh buon punto, questo è eccellente per generare finestre diverse sugli stessi due vettori. È anche utile per un conteggio dei turni variabile in runtime. –