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

时间:2013-07-23 11:21:12

标签: iphone c arm neon

我需要将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位数据加载到128位为32位?

修改

感谢@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 个答案:

答案 0 :(得分: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,但我希望将这些注册表用作uint32x4_t。”

即。它告诉编译器别名多组霓虹灯寄存器 - 阻止存储/加载到堆栈中,如果你通过它进行显式子集访问.val[...]语法。

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