我想使用Intel内在函数(16位无符号整数向量)进行一些操作,操作如下:
从unsigned short int数组加载或设置。
使用unsigned short int的Div和Mod操作。
使用unsigned short int的乘法运算。
将unsigned short int的操作存储到数组中。
我查看了Intrinsics指南,但看起来只有短整数的内在函数而不是未签名的整数。有人可以帮我解决这个问题吗?
实际上,我正在尝试将特定栅格格式的图像存储在具有特定排序的数组中。所以我必须计算每个像素值将被存储的索引:
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;
}
如果您只想要结果的低半部分,则乘法与有符号或无符号的二进制运算相同。所以你可以使用pmullw
。尽管如此,有单独的高半乘法指令用于有符号和无符号短路:_mm_mulhi_epu16
(pmulhuw
)与_mm_mulhi_epi16
(pmuluw
)
类似地,你不需要一个_mm_set_epu16
,因为它是相同的操作:在x86上转换为signed不会改变位模式,所以英特尔只打算提供_mm_set_epi16
,但是你可以使用像0xFFFFu
而不是-1
这样的args没问题。 (自动使用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效率较低(同样也适用于整数) ),以及可能更多的指令,因为一些除数需要比其他除数更复杂的序列。