使用Intel Intrinsics进行无符号短整数运算

时间:2017-12-20 21:09:40

标签: c sse pixels intrinsics avx

我想使用Intel内在函数(16位无符号整数向量)进行一些操作,操作如下:

来自unsigned short int数组的

加载设置

使用unsigned short int

Div Mod 操作。

使用unsigned short int

乘法操作。

将unsigned short int的操作操作到数组中。

我查看了内在函数指南,但看起来只有短整数的内在函数而不是未签名的整数。有人可以帮我解决这个问题吗?

实际上,我正在尝试将特定栅格格式的图像存储在具有特定排序的数组中。所以我必须计算每个像素值将被存储的索引:

unsigned int Index(unsigned int interleaving_depth, unsigned int x_size, unsigned int y_size, unsigned int z_size, unsigned int Pixel_number)
{
   unsigned int x = 0, y = 0, z = 0, reminder = 0, i = 0;

   y = Pixel_number/(x_size*z_size);
   reminder = Pixel_number % (x_size*z_size);

   i = reminder/(x_size*interleaving_depth);
   reminder = reminder % (x_size*interleaving_depth);

   if(i == z_size/interleaving_depth){
       x = reminder/(z_size - i*interleaving_depth);
       reminder = reminder % (z_size - i*interleaving_depth);
    }
    else
    {
       x = reminder/interleaving_depth;
       reminder = reminder % interleaving_depth;        
    }

    z = interleaving_depth*i + reminder;
    if(z >= z_size)
       z = z_size - 1;

    return x + y*x_size + *x_size*y_size;
}

1 个答案:

答案 0 :(得分:3)

如果您只想要结果的低半部分,则乘法与有符号或无符号的二进制运算相同。因此,您可以使用pmullw。但是,对于签名和无符号短片,有单独的高半乘法指令:_mm_mulhi_epu16pmulhuw)与_mm_mulhi_epi16pmuluw

同样,您不需要_mm_set_epu16,因为它的操作相同:在x86上转换为签名并不会改变位模式,因此英特尔只打算提供_mm_set_epi16,但您可以使用0xFFFFu之类的args代替-1而没有任何问题。 (自动使用Intel内在函数意味着您的代码只能移植到x86 32和64位。)

加载/存储内在函数根本不会更改数据。

SSE / AVX没有整数除法或mod指令。如果你有编译时常数除数,可以用乘法/移位自己做。您可以查看编译器输出以获得魔术常量和移位计数(Why does GCC use multiplication by a strange number in implementing integer division?),甚至让gcc为您自动矢量化。甚至可以使用GNU C本机向量语法来划分:

#include <immintrin.h>

__m128i div13_epu16(__m128i a) 
{
    typedef unsigned short __attribute__((vector_size(16))) v8uw;
    v8uw tmp = (v8uw)a;
    v8uw divisor = (v8uw)_mm_set1_epi16(13);
    v8uw result = tmp/divisor;
    return (__m128i)result;

    // clang allows "lax" vector type conversions without casts
    // gcc allows vector / scalar, e.g. tmp / 13.  Clang requires set1

    // to work with both, we need to jump through all the syntax hoops
}

使用gcc和clang(Godbolt compiler explorer)编译此asm:

div13_epu16:
    pmulhuw xmm0, XMMWORD PTR .LC0[rip]       # tmp93,
    psrlw   xmm0, 2       # tmp95,
    ret

.section .rodata
.LC0:
    .value  20165
    # repeats 8 times

如果你有运行时变量除数,它会变慢,但你可以使用http://libdivide.com/。如果重复使用相同的除数,那也不算太糟糕,所以你只需要为它计算一次定点逆,但是使用任意逆的代码需要一个变量移位计数,这对于SSE效率较低(也适用于整数),以及可能更多的指令,因为有些除数需要比其他除数更复杂的序列。