我需要将 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);
这很好用,但在我看来这种方法并不是最快的。也许有更好更快的方法将 8 位数据作为 32 位加载到 128 reg 中?
编辑:
感谢@FrankH。我使用一些 hack 想出了第二个版本:
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。
最佳答案
你可以用零做一个“double zip”:
uint16x4_t zero = 0;
uint32x4_t ld32x4 =
vreinterpretq_u32_u16(
vzipq_u8(
vzip_u8(
vld1_u8(arr),
vreinterpret_u8_u16(zero)
),
zero
)
);
由于 vreinterpretq_*()
是空操作,这归结为三个指令。目前没有交叉编译器,无法验证:(
编辑:
别误会我的意思……虽然 vreinterpretq_*()
不会产生 Neon 指令,但它不是空操作;那是因为它阻止 编译器执行您在使用 widerVal.val[0]
时会看到的那些奇怪的事情。它只告诉编译器,例如:
“你有一个 uint8x16x2_t
,但我只想使用其中的一半作为 uint8x16_t
,给我一半的寄存器。”
或者:
“您有一个 uint8x16x2_t
,但我想将这些 regs 用作 uint32x4_t
。”
即它告诉编译器 alias NEON 寄存器集 - 防止 存储/加载到堆栈/从堆栈加载,如果您通过.val[...]
语法。
在某种程度上,.val[...]
语法“是一种 hack”,但更好 方法是使用 vreinterpretq_*()
,“看起来像个 hack”。 不使用它会导致更多的指令和更慢/更差的代码。
关于iphone - NEON:将 uint8_t 数组加载到 128 位寄存器中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17808769/