NEON:将 uint8_t 数组加载到 128 位寄存器中

Posted

技术标签:

【中文标题】NEON:将 uint8_t 数组加载到 128 位寄存器中【英文标题】:NEON: loading uint8_t array into 128 bit register 【发布时间】:2013-07-23 11:21:11 【问题描述】:

我需要将 uint8 数组中的值加载到 128 NEON 寄存器中。有一个类似的question。但没有好的答案。

我的解决办法是:

uint8_t arr[4] = 1,2,3,4;

//load 4 of 8-bit vals into 64 bit reg
uint8x8_t _vld1_u8 = vld1_u8(arr);

//convert to 16-bit and move to 128-bit reg
uint16x8_t _vmovl_u8 = vmovl_u8(_vld1_u8);

//get low 64 bit and move them to 64-bit reg
uint16x4_t _vget_low_u16 = vget_low_u16(_vmovl_u8);

//convert to 32-bit and move to 128-bit reg
uint32x4_t ld32x4 = vmovl_u16(_vget_low_u16);

这很好用,但在我看来,这种方法并不是最快的。也许有更好更快的方法将 8bit 数据加载到 128 reg 作为 32bit ?

编辑:

感谢@FrankH。我用一些技巧想出了第二个版本:

uint8x16x2_t z = vzipq_u8(vld1q_u8(arr), q_zero);
uint8x16_t rr = *(uint8x16_t*)&z;
z = vzipq_u8(rr, q_zero);
ld32x4 = *(uint8x16_t*)&z;

归结为这个程序集(当编译器优化开启时):

vld1.8 d16, d17, [r5]
vzip.8 q8, q9
vorr   q9, q4, q4
vzip.8 q8, q9

因此没有多余的存储,而且速度非常快。但它仍然比第一个解决方案慢大约 x1.5。

【问题讨论】:

【参考方案1】:

你可以用零做一个“双拉链”:

uint16x4_t zero = 0;

uint32x4_t ld32x4 =
    vreinterpretq_u32_u16(
        vzipq_u8(
            vzip_u8(
                vld1_u8(arr),
                vreinterpret_u8_u16(zero)
            ),
            zero
        )
    );

由于vreinterpretq_*() 是无操作的,这归结为三个指令。目前没有交叉编译器,无法验证:(

编辑: 不要误会我的意思……虽然vreinterpretq_*() 不会产生霓虹灯指令,但它不是是无操作的;那是因为它阻止编译器执行如果你改用widerVal.val[0] 时你会看到的那种时髦的事情。它告诉编译器的只是:

“你有一个uint8x16x2_t,但我只想使用其中的一半作为uint8x16_t,给我一半的寄存器。”

或者:

“您有一个 uint8x16x2_t,但我想将这些 reg 用作 uint32x4_t。”

即它告诉编译器 alias 套霓虹灯寄存器 - preventing 存储/加载到堆栈/从堆栈加载,如果您通过.val[...] 语法。

在某种意义上,.val[...] 语法“是一个 hack”,但 更好 方法,使用 vreinterpretq_*(),“看起来像一个 hack”。 使用它会导致更多指令和更慢/更差的代码。

【讨论】:

但是 vzip_u8 返回 uint8x8x2_t 而 vzipq_u8 需要 uint8x16_t。 试过这个:ld32x4 = vzipq_u8(vzipq_u8(vld1q_u8(arr), q_zero).val[0], q_zero).val[0];但它比我的变体慢了大约 30%。还是谢谢! 不 - 不要做 .val[...] 的事情。这将强制存储/重新加载。使用vreinterpretq_*() - 将uint8x8x2_t“转换”为uint8x16_t,将uint8x16x2_t“转换”为uint8x32_t等...根据类型/大小,它所做的只是告诉编译器解释集合两个/四个霓虹灯调节器以不同的方式。 我试图转换这个:uint8x8x2_t in_v; uint8x16_t out_v = vreinterpretq_u8_u16(in_v);存在编译器错误:“没有要调用的匹配函数”。 啊……我明白了;问题:***.com/questions/13711407/…

以上是关于NEON:将 uint8_t 数组加载到 128 位寄存器中的主要内容,如果未能解决你的问题,请参考以下文章

将 Intrinsic xmm 寄存器转换为 uint8_t 数组[16]

NEON:如何将 128 位 ARGB 转换为具有饱和度的 32 位 ARGB?

使用 NEON/ARM 加载 8 位值

用元素加载 std::queue<uint8_t*> 的更有效方法?

与 ARM Neon vtbx 的字节顺序混淆

用 neon 内部函数替换 memcpy