Есть ли способ выполнить сравнение типа 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) или C >= (A+B)? В первом случае я не вижу, как добавление с насыщением приводит к ложным срабатываниям.
Отредактировано - его C >= (A + B)
Я думаю, что C-A >= B (с насыщенным вычитанием) должно работать (не проверено). Редактировать: Нет, это не так (нужно подумать об этом больше)
@chtz: если мы знаем, что C не может быть 0xffff, поможет ли это выполнить насыщение A+B, а затем сдвинуть по диапазону как C, так и сумму до знака (путем перестановки их битов знака с pxor) для pcmpgtw? Но если это должно работать для C = 0xffff, как и результат насыщения, я не думаю, что это помогает.
@PeterCordes Да, и трюк C-A >= B сработает, если гарантировано одно из B>0 или C>=A. (аналогично C-B >= A, конечно). Можно проверить C-min(A,B) >= max(A,B), что будет 5 мкп, если я правильно считаю.





Вот несколько разумных подходов:
#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 Что ты имеешь в виду? godbolt.org/z/z6hGzE
Спасибо. Это была проблема с моим тестом. Это приемлемый ответ для меня.
Я думаю, вы должны сначала проверить переполнение в соответствии с вопросом, который вы связали. Если вы обнаружите переполнение, вы знаете, что
C > (A + B)ложно. В противном случае проверьте следующее. Поскольку вы работаете с векторами, вам, возможно, придется выполнять обе проверки и объединять их с помощью побитовых операций. (Отредактировано, чтобы исправить обратное условие).