SSE2内在函数 - 比较无符号整数

uv_*_*uv_ 3 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);
Run Code Online (Sandbox Code Playgroud)

我会感兴趣的是执行比这些无符号整数更少的比较,类似于_mm_cmplt_epi8签名:

__m128i mask = _mm_cmplt_epi8 (m3, m1);
m1 = _mm_or_si128(m3, mask);
Run Code Online (Sandbox Code Playgroud)

如果"epu8"等效可用,mask必须0xFF在那里m3[i] < m1[i](溢出!) 0x00 otherwise,我们将能够饱和m1使用"或",所以m1将持有另外的结果,其中有效的,0xFF它溢出.

问题是,_mm_cmplt_epi8执行符号比较,因此,例如,如果m1[i] = 0x70m2[i] = 0x10,然后m3[i] = 0x80mask[i] = 0xFF,这显然不是我的要求.

使用VS2012.

我希望采用另一种方法来执行此操作.谢谢!

Pau*_*l R 8

实现无符号8位向量的比较的一种方法是利用_mm_max_epu8,其返回无符号8位int元素的最大值.您可以比较两个元素的(无符号)最大值与其中一个源元素的相等性,然后返回相应的结果.这转换为2个>=或的指令<=,以及3个>或的指令<.

示例代码:

#include <stdio.h>
#include <emmintrin.h>    // SSE2

#define _mm_cmpge_epu8(a, b) \
        _mm_cmpeq_epi8(_mm_max_epu8(a, b), a)

#define _mm_cmple_epu8(a, b) _mm_cmpge_epu8(b, a)

#define _mm_cmpgt_epu8(a, b) \
        _mm_xor_si128(_mm_cmple_epu8(a, b), _mm_set1_epi8(-1))

#define _mm_cmplt_epu8(a, b) _mm_cmpgt_epu8(b, a)

int main(void)
{
    __m128i va = _mm_setr_epi8(0,   0,   1,   1,   1, 127, 127, 127, 128, 128, 128, 254, 254, 254, 255, 255);
    __m128i vb = _mm_setr_epi8(0, 255,   0,   1, 255,   0, 127, 255,   0, 128, 255,   0, 254, 255,   0, 255);

    __m128i v_ge = _mm_cmpge_epu8(va, vb);
    __m128i v_le = _mm_cmple_epu8(va, vb);
    __m128i v_gt = _mm_cmpgt_epu8(va, vb);
    __m128i v_lt = _mm_cmplt_epu8(va, vb);

    printf("va   = %4vhhu\n", va);
    printf("vb   = %4vhhu\n", vb);
    printf("v_ge = %4vhhu\n", v_ge);
    printf("v_le = %4vhhu\n", v_le);
    printf("v_gt = %4vhhu\n", v_gt);
    printf("v_lt = %4vhhu\n", v_lt);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译并运行:

$ gcc -Wall _mm_cmplt_epu8.c && ./a.out 
va   =    0    0    1    1    1  127  127  127  128  128  128  254  254  254  255  255
vb   =    0  255    0    1  255    0  127  255    0  128  255    0  254  255    0  255
v_ge =  255    0  255  255    0  255  255    0  255  255    0  255  255    0  255  255
v_le =  255  255    0  255  255    0  255  255    0  255  255    0  255  255    0  255
v_gt =    0    0  255    0    0  255    0    0  255    0    0  255    0    0  255    0
v_lt =    0  255    0    0  255    0    0  255    0    0  255    0    0  255    0    0
Run Code Online (Sandbox Code Playgroud)

  • @PaulR并不缺少它们.咳... 64位算术右移.非对称整数/浮点置换.double < - > int64转换.如果只有英特尔会停止用AVX512拖动它的腿,这几乎是我想要的东西...... (2认同)

Pet*_*des 5

其他答案让我想到了一种更简单的方法来更直接地回答具体问题:

要简单地检测夹紧,请进行饱和和非饱和添加,并比较结果.

__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);
Run Code Online (Sandbox Code Playgroud)

所以这只是两个以外的指令adds,其中一个可以与之并行运行adds.因此,non_clipped在添加结果后一个周期就准备好了掩模.(可能有3条指令(一个额外的movdqa)没有AVX 3操作数非破坏性矢量操作.)

如果非饱和加法结果为0xFF,则它将与饱和加法结果匹配,并被检测为不剪切.这就是为什么它只是检查饱和加法输出为0xFF字节的原因.