Сравнение целых чисел без знака SSE4.1 с переполнением

Есть ли способ выполнить сравнение, подобное C ›= (A + B) с инструкциями SSE2/4.1, учитывая, что 16-битное сложение без знака (_mm_add_epi16()) может переполниться?

Фрагмент кода выглядит так:

#define _mm_cmpge_epu16(a, b) _mm_cmpeq_epi16(_mm_max_epu16(a, b), a)

__m128i *a = (__m128i *)&ptr1;
__m128i *b = (__m128i *)&ptr2;
__m128i *c = (__m128i *)&ptr3;
            
_m128i xa = _mm_lddqu_si128(a);
_m128i xb = _mm_lddqu_si128(b);
_m128i xc = _mm_lddqu_si128(c);

_m128i res = _mm_add_epi16(xa, xb);
_m128i xmm3 = _mm_cmpge_epu16(xc, res);

Проблема заключается в том, что при переполнении 16-битного сложения (переходе) сравнение больше приводит к ложным срабатываниям. Я не могу использовать насыщенную добавку для своей цели. Я рассмотрел механизм обнаружения переполнения для добавления без знака здесь проверка целочисленного переполнения SSE2 . Но как мне использовать if для большего, чем сравнение.


person Kaustubh    schedule 17.12.2020    source источник
comment
Я думаю, вы должны сначала проверить переполнение в соответствии с вопросом, который вы связали. Если вы обнаружите переполнение, вы знаете, что C > (A + B) ложно. В противном случае проверьте следующее. Поскольку вы работаете с векторами, вам, возможно, придется выполнять обе проверки и объединять их с помощью побитовых операций. (Отредактировано, чтобы исправить обратное условие).   -  person Jester    schedule 17.12.2020
comment
Вы хотите проверить C > (A+B) или C >= (A+B)? В первом случае я не вижу, как добавление с насыщением приводит к ложным срабатываниям.   -  person chtz    schedule 17.12.2020
comment
Отредактировано - его C ›= (A + B)   -  person Kaustubh    schedule 17.12.2020
comment
Я думаю, что C-A >= B (с насыщенным вычитанием) должен работать (не проверено). Редактировать: Нет, это не так (нужно подумать об этом больше)   -  person chtz    schedule 17.12.2020
comment
@chtz: если мы знаем, что C не может быть 0xffff, поможет ли это выполнить насыщение A+B, а затем сдвинуть по диапазону как C, так и сумму со знаком (путем перестановки их битов знака с pxor) для pcmpgtw? Но если это должно работать для C = 0xffff, как и результат насыщения, я не думаю, что это помогает.   -  person Peter Cordes    schedule 18.12.2020
comment
@PeterCordes Да, и трюк C-A >= B сработает, если гарантировано одно из B>0 или C>=A. (Аналогично для C-B >= A, конечно). Можно было бы проверить C-min(A,B) >= max(A,B), что будет 5 мкп, если я правильно считаю.   -  person chtz    schedule 18.12.2020


Ответы (2)


Вы строите недостающие примитивы из того, что у вас есть в наборе инструкций. Вот одна из возможных реализаций, непроверенная. Разборка.

// Compare uint16_t lanes for a >= b
inline __m128i cmpge_epu16( __m128i a, __m128i b )
{
    const __m128i max = _mm_max_epu16( a, b );
    return _mm_cmpeq_epi16( max, a );
}

// Compare uint16_t lanes for c >= a + b, with overflow handling
__m128i cmpgeSum( __m128i a, __m128i b, __m128i c )
{
    // Compute c >= a + b, ignoring overflow issues
    const __m128i sum = _mm_add_epi16( a, b );
    const __m128i ge = cmpge_epu16( c, sum );

    // Detect overflow of a + b
    const __m128i sumSaturated = _mm_adds_epu16( a, b );
    const __m128i sumInRange = _mm_cmpeq_epi16( sum, sumSaturated );

    // Combine the two
    return _mm_and_si128( ge, sumInRange );
}
person Soonts    schedule 17.12.2020
comment
Просто крайний случай, когда c равен 0xFFFF, а a+b переполняется. - person Kaustubh; 18.12.2020
comment
@Kaustubh Что ты имеешь в виду? godbolt.org/z/z6hGzE - person Soonts; 19.12.2020
comment
Спасибо. Это была проблема с моим тестом. Это приемлемый ответ для меня. - person Kaustubh; 19.12.2020

Вот несколько разумных подходов:

#include <cstdint>
using v8u16 = uint16_t __attribute__((vector_size(16)));

v8u16 lthsum1(v8u16 a, v8u16 b, v8u16 c) {
    return (c >= a) & (c - a >= b);
}

v8u16 lthsum2(v8u16 a, v8u16 b, v8u16 c) {
    return (a + b >= a) & (a + b <= c);
}

Вы можете увидеть, как это компилируется на godbolt. Оба подхода в целом эквивалентны, и я не вижу больших изменений с -msse4.1 с gcc, но AVX2 и более поздние версии улучшают код. clang также получает небольшие улучшения с sse4.1 для второго варианта. С AVX512BW clang неплохо себя зарекомендовал.

person EOF    schedule 17.12.2020