4

_mm_add_epi16()考虑到 16 位无符号加法 ( ) 会溢出,有什么方法可以将 C >= (A + B) 与 SSE2/4.1 指令进行比较?

代码片段看起来像 -

#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 整数溢出检查中查看了检测无符号加法溢出的机制。但是如果大于比较,我该如何使用。

4

2 回答 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);
}

你可以看到它是如何在 Godbolt上编译的。这两种方法大体上是等效的,我没有看到-msse4.1gcc 有很大的变化,但是 AVX2 和更高版本确实改进了代码。对于第二个变体, clang还通过 sse4.1 获得了一些小的改进。使用AVX512BW,clang 本身就做得很好。

于 2020-12-17T16:40:25.670 回答
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 );
}
于 2020-12-17T17:11:22.190 回答