У меня есть функция, использующая SSE для многих вещей, и профилировщик показывает мне, что часть кода, которую я использую для вычисления горизонтального минимума и максимума, потребляет большую часть времени.
Я использовал следующую реализацию для минимума, например:
static inline int16_t hMin(__m128i buffer) {
buffer = _mm_min_epi8(buffer, _mm_shuffle_epi8(buffer, m1));
buffer = _mm_min_epi8(buffer, _mm_shuffle_epi8(buffer, m2));
buffer = _mm_min_epi8(buffer, _mm_shuffle_epi8(buffer, m3));
buffer = _mm_min_epi8(buffer, _mm_shuffle_epi8(buffer, m4));
return ((int8_t*) ((void *) &buffer))[0];
}
Мне нужно вычислить минимум и максимум 16 1-байтовых целых чисел, как вы видите.
Любые хорошие предложения высоко ценятся 🙂
Спасибо
Я предлагаю два изменения:
((int8_t*) ((void *) &buffer))[0]
с _mm_cvtsi128_si32
,замещать _mm_shuffle_epi8
с _mm_shuffle_epi32
/_mm_shufflelo_epi16
которые имеют меньшую задержку на последних процессорах AMD и Intel Atom и сэкономят вам операции загрузки памяти:
static inline int16_t hMin(__m128i buffer)
{
buffer = _mm_min_epi8(buffer, _mm_shuffle_epi32(buffer, _MM_SHUFFLE(3, 2, 3, 2)));
buffer = _mm_min_epi8(buffer, _mm_shuffle_epi32(buffer, _MM_SHUFFLE(1, 1, 1, 1)));
buffer = _mm_min_epi8(buffer, _mm_shufflelo_epi16(buffer, _MM_SHUFFLE(1, 1, 1, 1)));
buffer = _mm_min_epi8(buffer, _mm_srli_epi16(buffer, 8));
return (int8_t)_mm_cvtsi128_si32(buffer);
}
SSE 4.1 имеет инструкцию, которая делает почти то, что вы хотите. Его имя PHMINPOSUW
, C / C ++ присущ _mm_minpos_epu16
, Он ограничен 16-битными значениями без знака и не может дать максимум, но эти проблемы могут быть легко решены.
_mm_srli_pi16
или же _mm_shuffle_epi8
, а потом _mm_min_epu8
чтобы получить 8 попарных минимальных значений в четных байтах и нули в нечетных байтах некоторого регистра XMM. (Эти нули создаются с помощью инструкции Shift / Shuffle и должны оставаться на своих местах после _mm_min_epu8
)._mm_minpos_epu16
найти минимум среди этих значений._mm_cvtsi128_si32
,Вот пример, который возвращает максимум 16 байтов со знаком:
static inline int16_t hMax(__m128i buffer)
{
__m128i tmp1 = _mm_sub_epi8(_mm_set1_epi8(127), buffer);
__m128i tmp2 = _mm_min_epu8(tmp1, _mm_srli_epi16(tmp1, 8));
__m128i tmp3 = _mm_minpos_epu16(tmp2);
return (int8_t)(127 - _mm_cvtsi128_si32(tmp3));
}