Получение минимального короткого значения в векторе __m128i с SSE?

Этот вопрос кажется похожим на Получить максимальное значение в векторе __m128i с SSE? но с шортами и минимумом вместо целого + максимум. Вот что я придумал:

typedef short int weight;

weight horizontal_min_Vec4i(__m128i x) {
__m128i max1 = _mm_shufflehi_epi16(x, _MM_SHUFFLE(0, 0, 3, 2));
__m128i max1b = _mm_shufflelo_epi16(x, _MM_SHUFFLE(0, 0, 3, 2));
__m128i max2 = _mm_min_epi16(max1, max1b);
//max2 = _mm_min_epi16(max2, x);
max1 = _mm_shufflehi_epi16(max2, _MM_SHUFFLE(0, 0, 0, 1));
max1b = _mm_shufflelo_epi16(max2, _MM_SHUFFLE(0, 0, 0, 1));
__m128i max3 = _mm_min_epi16(max1, max1b);
max2 = _mm_min_epi16(max2, max3);
return min(_mm_extract_epi16(max2, 0), _mm_extract_epi16(max2, 4));
}

Функция в основном делает то же самое, что и ответ в https://stackoverflow.com/a/18616825/1500111 для верхней и нижней частей х. Итак, я знаю, что минимальное значение находится в позиции 0 или 4 переменной __m128i max2. Хотя это намного быстрее, чем функция без SIMD horizontal_min_Vec4i_Plain(__m128i x) показано ниже, я боюсь, что узким местом является _mm_extract_epi16 operation на последней строке. Есть ли лучший способ добиться этого, для лучшего ускорения? Я использую Haswell, поэтому у меня есть доступ к последним расширениям SSE.

weight horizontal_min_Vec4i_Plain(__m128i x) {
weight result[8] __attribute__((aligned(16)));
_mm_store_si128((__m128i *) result, x);
weight myMin = result[0];
for (int l = 1; l < 8; l++) {
if (myMin > result[l]) {
myMin = result[l];
}
}
return myMin;
}

2

Решение

Сравнение со знаком и без знака почти одинаково, за исключением того, что диапазон с установленным верхним битом рассматривается как больший, чем диапазон с верхним битом, не установленным в сравнениях без знака, и как меньший в сравнениях со знаком. Это означает, что сравнения со знаком и без знака могут быть преобразованы друг в друга по следующим правилам:

x <s y = (x ^ signbit) <u (y ^ signbit)
x <u y = (x ^ signbit) <s (y ^ signbit)

Эта собственность переходит непосредственно к min а также max, так:

min_s(x, y) = min_u(x ^ signbit, y ^ signbit) ^ signbit

И тогда мы можем использовать _mm_minpos_epu16 обрабатывать горизонтальный минимум, чтобы получить, в целом, что-то вроде

__m128i xs = _mm_xor_si128(x, _mm_set1_epi16(0x8000));
return _mm_extract_epi16(_mm_minpos_epu16(xs), 0) - 0x8000;

- 0x8000 является ^ 0x8000 и знак-расширение (extract ноль-расширяет) свернул в одну.

2

Другие решения


По вопросам рекламы [email protected]