使用Intel SIMD SSE加载非连续值

时间:2016-03-17 16:42:09

标签: assembly x86 intel sse simd

我想加载一个带有32位非连续浮点数的128位寄存器。实际上,那些浮点数在内存中间隔128位。

所以如果内存看起来像那样:

| Float 0  | Float X | Float X | Float X |
| Float 4  | Float X | Float X | Float X |
| Float 8  | Float X | Float X | Float X |
| Float 12 | Float X | Float X | Float X |

我想加载这样的矢量:

| Float 0  | Float 4 | Float 8 | Float 12 |

2 个答案:

答案 0 :(得分:5)

希望您将其他数据用于某些事情,在这种情况下,加载所有内容并进行转置更有可能是有用的。

如果没有,那么只要数据在向量中有很多工作要做,那么SIMD根本就是可行的,因为将它打包到向量中是很昂贵的。

movss / insertps如@ zx485所示,答案是“正常”的方式,就像您使用_mm_set_ps(f[12], f[8], f[4], f[0]);时可能从编译器中得到的那样

当你的步幅正好是4时,使用AVX你可以用两个负载跨越所有四个浮点数并混合。

(相关:What's the fastest stride-3 gather instruction sequence?或者对于步幅2,更值得做矢量加载和改组。)

vmovups   ymm1, [float0]                  ; float0 and float4 in the low element of low/high lanes
vblendps  ymm1, [float8 - 4], 0b00100010  ;  { x x f12 f4 | x x f8 f0 }

这不是很好,因为您可能会与其中一个加载跨越缓存行边界。对于第二次加载,您可以使用vshufps ymm0, ymm1, [float8], 0b???????获得类似的东西。

这可能会很好,具体取决于周围的代码,特别是如果您有vpermps的AVX2(具有随机控制向量常量)或vpermpd(带有立即数)用于交叉路由的随机播放将你想要的元素放入低128b的通道。

如果没有AVX2进行跨车道随机播放,您需要vextractf128然后shufps。这可能需要提前做一些计划,以便在shufps可以将它们放在正确位置的位置放置元素。

当然,这一切都与内在函数一起使用,但它们需要更多的输入。

答案 1 :(得分:2)

如果你有AVX2可用,你可以使用VGATHERDPS指令来实现你的目标,解释here in this SO answer。在你的情况下,你只需要将index-vector初始化为0,1,2,3,...(并使用聚集寻址模式将其扩展到0,4,8,12)。

.data
  .align 16
  ddIndices dd 0,1,2,3
  dpValues  REAL4 ...   ; replace 'dpValues' with your value array
.code
  lea        rsi, dpValues
  vmovdqa    xmm7, ddIndices

.loop:
  vpcmpeqw   xmm1, xmm1                 ; set to all ones
  vpxor      xmm0, xmm0                 ; break dependency on previous gather
  vgatherdps xmm0, [rsi+xmm7*4], xmm1
  ; do something with gather result in xmm0

  add        rsi, 16
  cmp        rsi, end_pointer
  jb      .loop                    ; do another gather with same indices, base+=16

XMM1是condition mask,用于选择加载的元素。

请注意,这个指令在Haswell上并不是那么快,但是在Broadwell上实现速度更快,而在Skylake上实现速度更快。

即便如此,对于小步幅负载使用收集指令可能只是Skylake上8元素ymm向量的胜利。根据{{​​3}}( 11.16.4聚集指令的注意事项),当数据热时,具有4元素向量的Broadwell硬件采集具有每个元素1.56个周期的最佳情况吞吐量L1D缓存。

在AVX2之前的架构上,我没有办法(我知道)这样做而不会像这样单独加载所有值(使用SSE4.1 Intel's optimization manualpinsrd)。

lea      esi, dpValues
movss    xmm0, [esi]          ; breaks dependency on old value of xmm0
insertps xmm0, [esi+4], 1<<4  ; dst element index in bits 5:4 of the imm8
insertps xmm0, [esi+8], 2<<4
insertps xmm0, [esi+12], 3<<4

对于整数数据,最后一条指令为pinsrd xmm0, [esi+12], 3

如果没有SSE4.1,则将movss / unpcklps和/ {p>一起随机播放unpcklpd