2013-07-23 187 views
2

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

我的解决办法是:

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

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

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,但我想用这些暂存器为uint32x4_t来代替。”

即它告诉编译器别名组霓虹灯寄存器 - 阻止存储/加载/从堆栈加载,如果你通过.val[...]语法做明确的子集访问,你会得到。

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

+0

但是vzip_u8返回uint8x8x2_t,而vzipq_u8需要uint8x16_t。 – Max

+0

试过这个:ld32x4 = vzipq_u8(vzipq_u8(vld1q_u8(arr),q_zero).val [0],q_zero).val [0];但它比我的变体慢了约30%。不管怎么说,还是要谢谢你! – Max

+0

不 - 不要做'.val [...''的事。这将强制存储/重新加载。使用'vreinterpretq _ *()' - 根据类型/大小将uint8x8x2_t转换为uint8x16_t和uint8x16x2_t成为uint8x32_t等等,它只是告诉编译器以不同的方式解释一组两个/四个霓虹灯区域。 –

相关问题