7

我正在尝试使用 Neon 内在函数优化我的代码。我在 128 位数组(每个 8 位)上进行 24 位旋转uint16_t

这是我的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 是否有更好的文档?

4

3 回答 3

6

在阅读了Arm 社区博客之后,我发现了这一点:

霓虹灯臂按位旋转

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);
于 2012-06-30T11:49:59.063 回答
4

我不是 100% 确定,但我认为 NEON 没有旋转指令。

您可以使用左移、右移和或来组合所需的旋转操作,例如:

uint8_t ror(uint8_t in, int rotation)
{
    return (in >> rotation) | (in << (8-rotation));
}

只需对 Neon 内在函数执行相同的操作即可进行左移、右移和或。

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。

于 2012-06-30T03:22:20.350 回答
3

用于vext.8将向量与其自身连接,并为您提供所需的 16 字节窗口(在本例中偏移 3 个字节)。

使用内在函数执行此操作需要强制转换以使编译器满意,但它仍然是一条指令:

#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在 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无法使用奇数计数,需要额外的指令。 手册vext.8

VEXT 伪指令

您可以将数据类型指定为 16、32 或 64,而不是 8。在这种情况下,#imm 指的是半字、字或双字,而不是指字节,并且允许的范围相应减小。

于 2017-11-23T07:33:11.660 回答