Сравнение целых чисел без знака 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 для большего, чем сравнение.

Я думаю, вы должны сначала проверить переполнение в соответствии с вопросом, который вы связали. Если вы обнаружите переполнение, вы знаете, что C > (A + B) ложно. В противном случае проверьте следующее. Поскольку вы работаете с векторами, вам, возможно, придется выполнять обе проверки и объединять их с помощью побитовых операций. (Отредактировано, чтобы исправить обратное условие).

Jester 17.12.2020 16:43

Вы хотите проверить C > (A+B) или C >= (A+B)? В первом случае я не вижу, как добавление с насыщением приводит к ложным срабатываниям.

chtz 17.12.2020 16:53

Отредактировано - его C >= (A + B)

Kaustubh 17.12.2020 17:02

Я думаю, что C-A >= B (с насыщенным вычитанием) должно работать (не проверено). Редактировать: Нет, это не так (нужно подумать об этом больше)

chtz 17.12.2020 17:05

@chtz: если мы знаем, что C не может быть 0xffff, поможет ли это выполнить насыщение A+B, а затем сдвинуть по диапазону как C, так и сумму до знака (путем перестановки их битов знака с pxor) для pcmpgtw? Но если это должно работать для C = 0xffff, как и результат насыщения, я не думаю, что это помогает.

Peter Cordes 18.12.2020 07:32

@PeterCordes Да, и трюк C-A >= B сработает, если гарантировано одно из B>0 или C>=A. (аналогично C-B >= A, конечно). Можно проверить C-min(A,B) >= max(A,B), что будет 5 мкп, если я правильно считаю.

chtz 18.12.2020 09:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
6
248
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

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

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

Ответ принят как подходящий

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

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

Просто крайний случай, когда c равен 0xFFFF, а a+b переполняется.

Kaustubh 18.12.2020 16:06

@Kaustubh Что ты имеешь в виду? godbolt.org/z/z6hGzE

Soonts 18.12.2020 22:30

Спасибо. Это была проблема с моим тестом. Это приемлемый ответ для меня.

Kaustubh 19.12.2020 13:56

Другие вопросы по теме