我正在尝试使用Neon内在函数优化我的代码。我在128位阵列上进行24位旋转(每个uint16_t
8个)。
这是我的c代码:
uint16_t rotated[8];
uint16_t temp[8];
uint16_t j;
for(j = 0; j < 8; j++)
{
//Rotation <<< 24 over 128 bits (x << shift) | (x >> (16 - shift)
rotated[j] = ((temp[(j+1) % 8] << 8) & 0xffff) | ((temp[(j+2) % 8] >> 8) & 0x00ff);
}
我查看了有关Neon Intrinsics的gcc文档,但没有关于向量旋转的说明。此外,我尝试使用vshlq_n_u16(temp, 8)
执行此操作,但移位到uint16_t
字之外的所有位都将丢失。
如何使用霓虹内在函数实现这一目标?那么有关于GCC Neon Intrinsics的更好的文档吗?
答案 0 :(得分:6)
在对Arm Community Blogs进行一些阅读后,我发现了这一点:
VEXT:提取 VEXT从一对现有向量中提取新的字节向量。新向量中的字节来自第一个操作数的顶部和第二个操作数的底部。这允许您生成包含跨越一对现有向量的元素的新向量。 VEXT可用于实现两个矢量数据的移动窗口,在FIR滤波器中很有用。 对于置换,当对两个输入操作数使用相同的向量时,它也可用于模拟逐字节旋转操作。
以下Neon GCC Intrinsic与图片中提供的程序集相同:
uint16x8_t vextq_u16 (uint16x8_t, uint16x8_t, const int)
因此,完整的128位向量(不是在每个元素上)的24位旋转可以通过以下方式完成:
uint16x8_t input;
uint16x8_t t0;
uint16x8_t t1;
uint16x8_t rotated;
t0 = vextq_u16(input, input, 1);
t0 = vshlq_n_u16(t0, 8);
t1 = vextq_u16(input, input, 2);
t1 = vshrq_n_u16(t1, 8);
rotated = vorrq_u16(t0, t1);
答案 1 :(得分:4)
我不是100%肯定,但我不认为NEON有旋转说明。
您可以使用左移,右撇子和或等组合所需的旋转操作:
uint8_t ror(uint8_t in, int rotation)
{
return (in >> rotation) | (in << (8-rotation));
}
对于左移,右屎和或者。或者对霓虹内在函数做同样的事。
uint16x8_t temp;
uint8_t rot;
uint16x8_t rotated = vorrq_u16 ( vshlq_n_u16(temp, rot) , vshrq_n_u16(temp, 16 - rot) );
请参阅http://en.wikipedia.org/wiki/Circular_shift“实施循环转换。”
这将旋转通道内的值。如果您想要自动旋转车道,请使用其他答案中所述的VEXT。
答案 2 :(得分:2)
使用vext.8
与自身连接向量,并为您提供所需的16字节窗口(在这种情况下偏移3个字节)。
使用内在函数requires casting执行此操作以使编译器满意,但它仍然只是一条指令:
#include <arm_neon.h>
uint16x8_t byterotate3(uint16x8_t input) {
uint8x16_t tmp = vreinterpretq_u8_u16(input);
uint8x16_t rotated = vextq_u8(tmp, tmp, 16-3);
return vreinterpretq_u16_u8(rotated);
}
g++5.4 -O3 -march=armv7-a -mfloat-abi=hard -mfpu=neon
(on Godbolt)将其编译为:
byterotate3(__simd128_uint16_t):
vext.8 q0, q0, q0, #13
bx lr
16-3的计数意味着我们左旋3个字节。 (这意味着我们从左向量中获取13个字节,从右向量中获取3个字节,因此它也向右旋转13个。)
相关:x86还有一个指令,它将滑动窗口放入两个寄存器的串联中:palignr
(在SSSE3中添加)。
也许我错过了一些关于NEON的内容,但我不明白为什么OP的自我回答是使用vext.16
(vextq_u16),它具有16位粒度。它甚至不是一个不同的指令,只是vext.8
的别名,这使得无法使用奇数计数,需要额外的指令。 The manual for vext.8
says:
VEXT伪指令
您可以指定数据类型为16,32或64而不是8.在此处 case,#imm是指半字,单词或双字而不是 引用字节,相应地允许范围 降低。