【问题标题】:SSE4.1 unsigned integer comparison with overflowSSE4.1 无符号整数比较与溢出
【发布时间】:2021-03-28 07:20:23
【问题描述】:

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

代码sn-p看起来像-

#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 integer overflow checking 中查看了检测无符号加法溢出的机制。但是如何使用 if 进行大于比较。

【问题讨论】:

  • 我认为您应该首先根据您链接的问题检查溢出。如果您确实检测到溢出,您就知道C > (A + B) 是假的。否则,请检查下一步。由于您正在做向量,因此您可能必须执行这两项检查并使用按位运算合并它们。 (已编辑以修复反向条件)。
  • 您要查看C > (A+B) 还是C >= (A+B)?在第一种情况下,我看不出添加饱和度会导致误报。
  • 已编辑 - 它的 C >= (A + B)
  • 我认为C-A >= B(带有饱和减法)应该可以工作(未经测试)。编辑:不,它没有(需要更多考虑)
  • @PeterCordes 是的,如果B>0C>=A 之一得到保证,C-A >= B 技巧将起作用。 (当然,C-B >= A 也类似)。如果我计数正确,可以检查C-min(A,B) >= max(A,B),这将是 5 微秒。

标签: assembly x86 simd sse2 sse4


【解决方案1】:

这里有一些合理的方法:

#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 本身就做得很好。

【讨论】:

    【解决方案2】:

    您根据指令集中可用的内容构建缺少的原语。 这是一种可能的实现,未经测试。 Disassembly.

    // 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
    • 谢谢。这是我测试的问题。这是我的接受答案。
    猜你喜欢
    • 1970-01-01
    • 2012-02-29
    • 2014-06-08
    • 1970-01-01
    • 2017-10-19
    • 2013-04-10
    • 1970-01-01
    • 2015-02-08
    • 1970-01-01
    相关资源
    最近更新 更多