【发布时间】:2018-01-04 16:23:12
【问题描述】:
英特尔的向量扩展 SSE、AVX 等为每个元素大小提供了两个解包操作,例如SSE 内在函数是 _mm_unpacklo_* 和 _mm_unpackhi_*。对于一个向量中的 4 个元素,它会这样做:
inputs: (A0 A1 A2 A3) (B0 B1 B2 B3)
unpacklo/hi: (A0 B0 A1 B1) (A2 B2 A3 B3)
在 ARM 的 NEON 指令集中,unpack 的等价物是 vzip。但是,NEON 指令集也提供了操作vuzp,它是vzip 的逆操作。对于一个向量中的 4 个元素,它会这样做:
inputs: (A0 A1 A2 A3) (B0 B1 B2 B3)
vuzp: (A0 A2 B0 B2) (A1 A3 B1 B3)
如何使用 SSE 或 AVX 内部函数有效地实现 vuzp?似乎没有关于它的说明。对于 4 个元素,我假设可以使用 shuffle 和随后的 unpack 移动 2 个元素来完成:
inputs: (A0 A1 A2 A3) (B0 B1 B2 B3)
shuffle: (A0 A2 A1 A3) (B0 B2 B1 B3)
unpacklo/hi 2: (A0 A2 B0 B2) (A1 A3 B1 B3)
有没有使用单条指令的更有效的解决方案? (也许首先是 SSE - 我知道对于 AVX,我们可能会遇到额外的问题,即 shuffle 和 unpack 不会越过车道。)
了解这一点对于编写数据混杂和去混杂的代码可能很有用(应该可以通过基于解包操作反转混杂代码的操作来派生去混杂代码)。
编辑:这里是8元素版本:这是NEON的vuzp的效果:
input: (A0 A1 A2 A3 A4 A5 A6 A7) (B0 B1 B2 B3 B4 B5 B6 B7)
vuzp: (A0 A2 A4 A6 B0 B2 B4 B6) (A1 A3 A5 A7 B1 B3 B5 B7)
这是我的版本,每个输出元素有一个shuffle 和一个unpack(似乎可以推广到更大的元素数):
input: (A0 A1 A2 A3 A4 A5 A6 A7) (B0 B1 B2 B3 B4 B5 B6 B7)
shuffle: (A0 A2 A4 A6 A1 A3 A5 A7) (B0 B2 B4 B6 B1 B3 B5 B7)
unpacklo/hi 4: (A0 A2 A4 A6 B0 B2 B4 B6) (A1 A3 A5 A7 B1 B3 B5 B7)
EOF 建议的方法是正确的,但每个输出都需要 log2(8)=3 unpack 操作:
input: (A0 A1 A2 A3 A4 A5 A6 A7) (B0 B1 B2 B3 B4 B5 B6 B7)
unpacklo/hi 1: (A0 B0 A1 B1 A2 B2 A3 B3) (A4 B4 A5 B5 A6 B6 A7 B7)
unpacklo/hi 1: (A0 A4 B0 B4 A1 A5 B1 B5) (A2 A6 B2 B6 A3 A7 B3 B7)
unpacklo/hi 1: (A0 A2 A4 A6 B0 B2 B4 B6) (A1 A3 A5 A7 B1 B3 B5 B7)
【问题讨论】:
-
很高兴 AVX512 解决了所有这些问题。终于!
-
@Mysticial:AVX512 在哪些方面修复了它?是否有逆解包,还是他们放弃了面向车道的处理?关于后者:根据英特尔内部指南,例如在 AVX512 中,解包仍然是面向通道的(例如
_mm512_unpackhi_epi8:“从 a 和 b 中每个 128 位通道的高半部分解包并交错 8 位整数,并将结果存储在 dst 中。”)。 -
@Ralf Just
unpack[lo/hi]再次log2(vectorlength)次。 zip/unzip 是循环的。 -
@Ralf
vpermi2ps/vpermt2ps,vpermi2d/vpermt2d -
@BeeOnRope - NEON 的
vuzp实际上使用两个寄存器作为输入,使用相同的两个寄存器作为输出。英特尔指令/内在函数只有一个向量输出,因此,正如您所说,每行由两条指令生成(例如unpacklo和unpackhi)。所以最少是 2 条指令(例如 2 次shuffle_ps,正如 Peter Cordes 的回答),我的组合(shuffle加上unpack)使用 4。