是否有SIMD指令来实现批处理数组内存索引映射?

时间:2018-02-26 05:53:42

标签: sse simd neon

在我的RGB到灰色的情况下:

Y = (77*R + 150*G + 29*B) >> 8;

我知道SIMD(NEON,SSE2)可以这样做:

foreach 8 elements:
{A0,A1,A2,A3,A4,A5,A6,A7} = 77*{R0,R1,R2,R3,R4,R5,R6,R7}
{B0,B1,B2,B3,B4,B5,B6,B7} = 150*{G0,G1,G2,G3,G4,G5,G6,G7}
{C0,C1,C2,C3,C4,C5,C6,C7} = 29*{B0,B1,B2,B3,B4,B5,B6,B7}
{D0,D1,D2,D3,D4,D5,D6,D7} = {A0,A1,A2,A3,A4,A5,A6,A7} + {B0,B1,B2,B3,B4,B5,B6,B7}
{D0,D1,D2,D3,D4,D5,D6,D7} = {D0,D1,D2,D3,D4,D5,D6,D7} + {C0,C1,C2,C3,C4,C5,C6,C7}
{D0,D1,D2,D3,D4,D5,D6,D7} = {D0,D1,D2,D3,D4,D5,D6,D7} >> 8

但是,乘法指令至少需要2个时钟周期,而[0-255]中的R,G,B, 我们可以使用三个查找表(一个数组,长度= 256)来存储部分结果 77 * R(标记为X),150 * G(标记为Y),29 * B(标记为Z)。 因此,我正在寻找指示可以达到目的:

foreach 8 elements:
{A0,A1,A2,A3,A4,A5,A6,A7} = {X[R0],X[R1],X[R2],X[R3],X[R4],X[R5],X[R6],X[R7]}
{B0,B1,B2,B3,B4,B5,B6,B7} = {Y[G0],Y[G1],Y[G2],Y[G3],Y[G4],Y[G5],Y[G6],Y[G7]}
{C0,C1,C2,C3,C4,C5,C6,C7} = {Z[B0],Z[B1],Z[B2],Z[B3],Z[B4],Z[B5],Z[B6],Z[B7]}
{D0,D1,D2,D3,D4,D5,D6,D7} = {A0,A1,A2,A3,A4,A5,A6,A7} + {B0,B1,B2,B3,B4,B5,B6,B7}
{D0,D1,D2,D3,D4,D5,D6,D7} = {D0,D1,D2,D3,D4,D5,D6,D7} + {C0,C1,C2,C3,C4,C5,C6,C7}
{D0,D1,D2,D3,D4,D5,D6,D7} = {D0,D1,D2,D3,D4,D5,D6,D7} >> 8

有什么好的建议吗?

1 个答案:

答案 0 :(得分:0)

AVX2 / AVX512中没有字节或字汇集指令,NEON中根本没有收集。存在的DWORD聚集比乘法慢得多!例如根据{{​​3}},vpgatherdd ymm,[reg + scale*ymm], ymm每5个周期一个吞吐量。

您可以将shuffle用作并行表查找。但是每个查找的表是256个16位字。这是512个字节。 AVX512有一些混音可以从2个寄存器的串联中进行选择,但只有"只有" 2x64字节,其字节或字元素大小为Agner Fog's instruction table for Skylake。 (例如multiple uops on current CPUs)。不过,与vpshufb相比,它们仍然非常强大。

所以使用shuffle作为LUT在你的情况下不会起作用,但是对于某些情况,它确实很有用非常,例如对于popcount,你可以将字节分成4位半字节,并使用vpshufb从16字节的字节表中并行执行32次查找。

通常对于SIMD,您希望用计算替换表查找,因为计算对SIMD更友好。

吸吮并使用AVX512BW vpermi2w。您具有指令级并行性,而Skylake的每时钟吞吐量为2个,用于16位SIMD乘法(但周期延迟为5个周期)。对于图像处理,通常吞吐量比延迟更重要,只要您将延迟保持在合理范围内,因此乱序执行可以隐藏它。

如果乘法器的二进制表示中只有少量1位,则可以考虑使用shift / add而不是实际乘法。例如B * 29 = B * 32 - B - B * 2。或B<<5 - B<<1 - B。但是,许多指令可能比单个乘法具有更高的吞吐量成本。如果你只用2个术语来做,那可能是值得的。 (但话说再说一遍,仍然可能不是,取决于CPU。总指令吞吐量和矢量ALU瓶颈是一个大问题。)