_mm_alignr_epi8 (PALIGNR) эквивалент в AVX2
В SSE3 команда PALIGNR выполняет следующее:
PALIGNR объединяет операнд назначения (первый операнд) и исходный операнд (второй операнд) в промежуточный составной элемент, сдвигает композит на гранулярность байта вправо постоянной константой и извлекает выровненный по правому краю результат в место назначения.
В настоящее время я занимаюсь переносом моего кода SSE4 для использования инструкций AVX2 и работы с 256-битными регистрами вместо 128 бит.
Наивно, я полагал, что функция 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 (256 бит) на два xmm (128 бит) регистра (высокий и низкий), например:
__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, которая должна использоваться для получения того же результата?
Ответы
Ответ 1
Для чего вы используете 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
, я, вероятно, могу быть более полезным.
Ответ 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 байт.
Ответ 3
Нам нужны две инструкции: "vperm2i128" и "vpalignr" для расширения "palignr" на 256 бит.
Смотрите: https://software.intel.com/en-us/blogs/2015/01/13/programming-using-avx2-permutations