事实证明这并不简单。
简单有效的方法从简单地获取索引开始(只需使用位掩码加载0 1 2 3 4 5 6 7 和vand 的静态向量)。然而,为了在输出向量的一端收集它们——在与它们所代表的输入通道不同的通道中——你需要一些任意的置换操作。只有一条指令能够任意置换向量,vtbl(或vtbx,本质上是一回事)。但是,vtbl 采用目标顺序的源索引向量,结果与您尝试生成的内容完全相同。因此,为了产生最终结果,您需要使用最终结果,因此天真的有效解决方案是不可能的; QED。
根本问题是,您实际上所做的是排序一个向量,这本质上不是并行 SIMD 操作。 NEON 是专为媒体处理而设计的并行 SIMD 指令集,实际上并不适用于更一般的矢量处理的任何数据相关/水平/分散收集操作。
为了证明这一点,我确实设法在纯 NEON 中做到了这一点,根本没有任何标量代码,这可怕;我能想到的最好的“一个或两个位移位 NEON 指令”是一些基于条件选择的旋转位掩码累积技巧。如果不清楚,我建议在调试器或模拟器中逐步完成它的工作(example):
// d0 contains input vector
vmov.u8 d1, #0
vmov.u8 d2, #0
vmvn.u8 d3, #0
vdup.u8 d4, d0[0]
vext.u8 d5, d2, d3, #7
vbit.u8 d3, d5, d4
vsub.u8 d1, d1, d3
vdup.u8 d4, d0[1]
vext.u8 d5, d2, d3, #7
vbit.u8 d3, d5, d4
vsub.u8 d1, d1, d3
vdup.u8 d4, d0[2]
vext.u8 d5, d2, d3, #7
vbit.u8 d3, d5, d4
vsub.u8 d1, d1, d3
vdup.u8 d4, d0[3]
vext.u8 d5, d2, d3, #7
vbit.u8 d3, d5, d4
vsub.u8 d1, d1, d3
vdup.u8 d4, d0[4]
vext.u8 d5, d2, d3, #7
vbit.u8 d3, d5, d4
vsub.u8 d1, d1, d3
vdup.u8 d4, d0[5]
vext.u8 d5, d2, d3, #7
vbit.u8 d3, d5, d4
vsub.u8 d1, d1, d3
vdup.u8 d4, d0[6]
vext.u8 d5, d2, d3, #7
vbit.u8 d3, d5, d4
vsub.u8 d1, d1, d3
vdup.u8 d4, d0[7]
vext.u8 d5, d2, d3, #7
vbit.u8 d3, d5, d4
vbic.u8 d1, d1, d3
// d1 contains output vector
作弊和使用循环(这需要以相反的方向旋转d0,以便我们可以通过d0[0] 访问每个原始车道)使其更小,但实际上并没有那么糟糕:
vmov.u8 d1, #0
vmov.u8 d2, #0
vmvn.u8 d3, #0
mov r0, #8
1:
vdup.u8 d4, d0[0]
vext.u8 d5, d2, d3, #7
vbit.u8 d3, d5, d4
subs r0, r0, #1
vext.u8 d0, d0, d0, #1
vsub.u8 d1, d1, d3
bne 1b
vbic.u8 d1, d1, d3
理想情况下,如果有可能对算法的其他部分进行返工以避免需要向量的非常量排列,则改为这样做。