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] = 0x70和m2[i] = 0x10,然后m3[i] = 0x80和mask[i] = 0xFF,这显然不是我的要求.
使用VS2012.
我希望采用另一种方法来执行此操作.谢谢!
实现无符号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)
其他答案让我想到了一种更简单的方法来更直接地回答具体问题:
要简单地检测夹紧,请进行饱和和非饱和添加,并比较结果.
__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字节的原因.