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

时间:2015-10-05 09:40:05

标签: c++ x86 sse simd intrinsics

我有兴趣在添加无符号8位整数时识别溢出值,并将结果饱和到0xFF:

ALAsset URL,

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

ALAssetsLibrary *assetLibrary=[[ALAssetsLibrary alloc] init];

[assetLibrary assetForURL:self.videoURL resultBlock:^(ALAsset *asset) {
    NSArray *objectsToShare =  @[asset.defaultRepresentation.url];
    FrodoInstagramActivity *instagramActivity = [[FrodoInstagramActivity alloc] init];

    UIActivityViewController *activityVC = [[UIActivityViewController alloc] 
        initWithActivityItems:objectsToShare applicationActivities:@[instagramActivity]];
    [self presentViewController:activityVC animated:YES completion:nil];
} failureBlock:nil];

如果“epu8”等效版可用,__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); _mm_cmplt_epi8 __m128i mask = _mm_cmplt_epi8 (m3, m1); m1 = _mm_or_si128(m3, mask); (溢出!),mask,我们就能够0xFF饱和m3[i] < m1[i] 1}}使用“或”,因此0x00 otherwise会将添加结果保存在有效位置,并m1保留溢出的位置。

问题是,m1执行签名比较,例如0xFF_mm_cmplt_epi8,然后是m1[i] = 0x70m2[i] = 0x10,这显然不是什么我需要。

使用VS2012。

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

4 个答案:

答案 0 :(得分: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;
}

编译并运行:

$ 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

答案 1 :(得分: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);

因此,只有adds之外的两条指令,其中一条指令可以与adds并行运行。因此,non_clipped掩码在添加结果后的一个周期就绪。 (可能有3条指令(额外的movdqa),没有AVX 3操作数和非破坏性矢量操作。)

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

答案 2 :(得分:2)

比较无符号字节的另一种方法:添加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对齐,类似于this answer

答案 3 :(得分:0)

有一个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);
    }