C++ SSE2内部函数-比较无符号整数

C++ SSE2内部函数-比较无符号整数,c++,x86,sse,simd,intrinsics,C++,X86,Sse,Simd,Intrinsics,我感兴趣的是在添加无符号8位整数时识别溢出值,并将结果钳制为0xFF: __m128i m1 = _mm_loadu_si128(/* 16 8-bit unsigned integers */); __m128i m2 = _mm_loadu_si128(/* 16 8-bit unsigned integers */); __m128i m3 = _mm_adds_epu8(m1, m2); 我有兴趣对这些无符号整数执行“小于”的比较,类似于有符号整数的\u mm\u cmplt\u e

我感兴趣的是在添加无符号8位整数时识别溢出值,并将结果钳制为0xFF:

__m128i m1 = _mm_loadu_si128(/* 16 8-bit unsigned integers */);
__m128i m2 = _mm_loadu_si128(/* 16 8-bit unsigned integers */);

__m128i m3 = _mm_adds_epu8(m1, m2);
我有兴趣对这些无符号整数执行“小于”的比较,类似于有符号整数的
\u mm\u cmplt\u epi8

__m128i mask = _mm_cmplt_epi8 (m3, m1);
m1 = _mm_or_si128(m3, mask);
如果“epu8”等价物可用,
mask
将具有
0xFF
其中
m3[i]
(溢出!),
0x00
否则,我们将能够使用“or”钳制
m1
,因此
m1
将在有效时保存加法结果,在溢出时保存
0xFF

问题是,
\u mm\u cmplt\u epi8
执行有符号比较,因此,例如,如果
m1[i]=0x70
m2[i]=0x10
,那么
m3[i]=0x80
mask[i]=0xFF
,这显然不是我所需要的

使用VS2012


我希望能有另一种方法来执行此操作。谢谢

实现无符号8位向量比较的一种方法是利用
\u mm\u max\u epu8
,它返回最大的无符号8位int元素。您可以将两个元素的(无符号)最大值与其中一个源元素进行相等比较,然后返回相应的结果。这转化为两条关于
=
的说明。其他答案让我想到了一种更简单的方法来更直接地回答特定问题:

为了简单地检测箝位,进行饱和和非饱和加法,并比较结果

__m128i m1 = _mm_loadu_si128(/* 16 8-bit unsigned integers */);
__m128i m2 = _mm_loadu_si128(/* 16 8-bit unsigned integers */);

__m128i m1m2_sat = _mm_adds_epu8(m1, m2);
__m128i m1m2_wrap = _mm_add_epi8(m1, m2);
__m128i non_clipped = _mm_cmpeq_epi8(m1m2_sat, m1m2_wrap);
因此,除了
adds
之外只有两条指令,其中一条指令可以与
adds
并行运行。因此,
非剪裁的
掩码在加法结果一个周期后准备就绪。(可能有3条指令(额外的movdqa),不带AVX 3操作数非破坏性向量运算。)


如果非饱和添加结果为0xFF,则它将与饱和添加结果匹配,并被检测为未剪裁。这就是为什么它不同于只检查饱和加法的输出是否有0xFF字节。

比较无符号字节的另一种方法:添加
0x80
,并将它们作为有符号字节进行比较

__m128i _mm_cmplt_epu8(__m128i a, __m128i b) {
    __m128i as = _mm_add_epi8(a, _mm_set1_epi8((char)0x80));
    __m128i bs = _mm_add_epi8(b, _mm_set1_epi8((char)0x80));
    return _mm_cmplt_epi8(as, bs);
}
我不认为这是非常有效的,但它的工作,它可能是有用的,在某些情况下。此外,如果需要,可以使用xor代替加法。
在某些情况下,您甚至可以一次执行双向范围检查,即将值与下限和上限进行比较。为此,请将下限与0x80对齐,与此类似。

有一个8位无符号整数比较的实现:

    inline __m128i NotEqual8u(__m128i a, __m128i b)
    {
        return _mm_andnot_si128(_mm_cmpeq_epi8(a, b), _mm_set1_epi8(-1));
    }

    inline __m128i Greater8u(__m128i a, __m128i b)
    {
        return _mm_andnot_si128(_mm_cmpeq_epi8(_mm_min_epu8(a, b), a), _mm_set1_epi8(-1));
    }

    inline __m128i GreaterOrEqual8u(__m128i a, __m128i b)
    {
        return _mm_cmpeq_epi8(_mm_max_epu8(a, b), a);
    }

    inline __m128i Lesser8u(__m128i a, __m128i b)
    {
        return _mm_andnot_si128(_mm_cmpeq_epi8(_mm_max_epu8(a, b), a), _mm_set1_epi8(-1));
    }

    inline __m128i LesserOrEqual8u(__m128i a, __m128i b)
    {
        return _mm_cmpeq_epi8(_mm_min_epu8(a, b), a);
    }

当然,
\u mm\u添加已经饱和的结果。确定此处未描述的计算所需结果的位置。将结果与最大值进行比较以获得相等值。现在似乎是显而易见的:)谢谢你的详细回答@保罗,他们不缺。啊哼。。。64位算术右移。非对称整数/浮点置换。双int64转换。如果英特尔能停止使用AVX512的东西,它几乎拥有我想要的一切,那该多好啊…@PaulR Broadwell Xeon会有AVX512吗?我以为等待的是骑士登陆或天湖珀利。我认为故事的内容远不止这些。今年唯一的“Skylake Xeon”芯片是基于桌面部件(socket 1151)的芯片。有证据表明桌面Skylake甚至没有AVX512硅芯片。(而不是熔合关闭)看,我们不会看到它,直到服务器线(Skylake-E/Skylake Purley?)出来。英特尔在Skylake Purley上发布AVX512广告的事实可能意味着,除了Xeon Phi之外,它不会提前发布。哦,这太令人失望了。当联想等最近开始发布Skylake笔记本电脑时,我想我们可能已经接近了。那么,也许2016年就不会了?…这是一个更“全面”的答案!
__m128i _mm_cmplt_epu8(__m128i a, __m128i b) {
    __m128i as = _mm_add_epi8(a, _mm_set1_epi8((char)0x80));
    __m128i bs = _mm_add_epi8(b, _mm_set1_epi8((char)0x80));
    return _mm_cmplt_epi8(as, bs);
}
    inline __m128i NotEqual8u(__m128i a, __m128i b)
    {
        return _mm_andnot_si128(_mm_cmpeq_epi8(a, b), _mm_set1_epi8(-1));
    }

    inline __m128i Greater8u(__m128i a, __m128i b)
    {
        return _mm_andnot_si128(_mm_cmpeq_epi8(_mm_min_epu8(a, b), a), _mm_set1_epi8(-1));
    }

    inline __m128i GreaterOrEqual8u(__m128i a, __m128i b)
    {
        return _mm_cmpeq_epi8(_mm_max_epu8(a, b), a);
    }

    inline __m128i Lesser8u(__m128i a, __m128i b)
    {
        return _mm_andnot_si128(_mm_cmpeq_epi8(_mm_max_epu8(a, b), a), _mm_set1_epi8(-1));
    }

    inline __m128i LesserOrEqual8u(__m128i a, __m128i b)
    {
        return _mm_cmpeq_epi8(_mm_min_epu8(a, b), a);
    }