2011-12-15 21 views
7

В SSE3, инструкция PALIGNR выполняет следующие операции:_mm_alignr_epi8 (PALIGNR) эквивалент в 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.

Я в настоящее время в разгаре портирования моего SSE4 кода, чтобы использовать инструкции AVX2 и работают на 256bit регистров вместо 128bit. Наивно, я считал, что функция intrinsics _mm256_alignr_epi8 (VPALIGNR) выполняет ту же операцию, что и _mm_alignr_epi8, только на 256-битных регистрах. К сожалению, это не совсем так. Фактически, _mm256_alignr_epi8 рассматривает 256-битный регистр как 2 128-битные регистры и выполняет 2 операции «выравнивания» в двух соседних 128-битных регистрах. Эффективно выполняет ту же операцию, что и _mm_alignr_epi8, но на 2 регистрах одновременно. Это наиболее наглядно показано здесь: _mm256_alignr_epi8

В настоящее время мое решение продолжать использовать _mm_alignr_epi8 путем разделения YMM (256bit) регистрирует в двух XMM (128bit) регистры (высокий и низкий), например так:

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

Это работает, но должен быть лучший способ, не так ли? Возможно, существует более «общая» инструкция AVX2, которая должна использоваться для получения того же результата?

ответ

2

Единственное решение, которое я смог придумать для этого:

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; 
    } 
} 

, который я думаю, что это в значительной степени совпадает с вашим решением, за исключением также обрабатывает сдвиги> = 16 байт.

+0

yup, это то же решение. но если это единственный способ, то это выглядит как большой надзор со стороны разработчиков инструкций AVX2. – eladidan

+0

Я не мог заставить это скомпилировать ... Я получаю ошибку компиляции: «катастрофическая ошибка: Внутренний параметр должен быть немедленным значением» на следующей строке: «__m128i vouth = _mm_alignr_epi8 (v0l, v0h, n);». Существенно, потому что n не является абсолютным. Как вы могли обойти это? Я использую компилятор Intel C++ – eladidan

+0

Он работает для меня, если n - постоянная времени компиляции - я тоже использую компилятор Intel ICC, но компилирую как C, а не C++, если это имеет значение, и это также работает для меня с gcc. –

4

Вы используете palignr для? Если это нужно только для обработки несогласованности данных, просто используйте вместо них смещенные нагрузки; они, как правило, «достаточно быстро» на современных Intel-архитектурах (и сэкономит вам много размера кода).

Если вам нужно поведение palignr по какой-либо другой причине, вы можете просто воспользоваться поддержкой выравнивания без нагрузки, чтобы сделать это без ветвей. Если вы не полностью привязаны к загрузке, это, вероятно, предпочтительная идиома.

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]); 
} 

Если вы можете предоставить более подробную информацию о том, как именно вы используете palignr, я, вероятно, может быть более полезным.

+0

Задержка не будет очень хорошей, потому что нагрузка будет иметь дополнительные ~ 10 циклов латентности от магазина-переадресации на процессорах Intel. Тем не менее, IDK, если хранилища-хранилища-хранилища являются проблемой пропускной способности. Они могут и не быть. –

+1

@PeterCordes: нет пропускной способности, только латентность. Описанный здесь подход имеет смысл в ситуациях, когда хранилище можно поднимать, чтобы скрыть задержку, или сохраненные данные могут быть повторно использованы для извлечения множества различных выравниваний. Конечно, в AVX-512 есть два источника перетасовки, которые обычно являются лучшим решением. –

+0

О, хорошо, это отлично подходит для создания разных окон на те же два вектора. Это также полезно для подсчета переменных времени выполнения. –