Pin*_*kPR 5 x86 assembly sse intel simd
我想加载一个128位寄存器与32位非连续浮点数.实际上,那些浮点数在内存中间隔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 |
Run Code Online (Sandbox Code Playgroud)
我想加载这样的矢量:
| Float 0 | Float 4 | Float 8 | Float 12 |
Run Code Online (Sandbox Code Playgroud)
如果您有 AVX2 可用,您可以使用VGATHERDPS指令来实现您的目标,这在这个 SO 答案中进行了解释。在您的情况下,您只需将索引向量初始化为 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
Run Code Online (Sandbox Code Playgroud)
XMM1 是condition mask选择加载哪些元素的。
请注意,该指令在 Haswell 上速度不是那么快,但在 Broadwell 上实现速度更快,在 Skylake 上再次更快。
即便如此,在 Skylake 上使用聚集指令进行小步长加载可能只是 8 元素 ymm 向量的胜利。根据Intel 的优化手册(11.16.4 收集指令的注意事项),当 L1D 缓存中的数据很热时,具有 4 元素向量的 Broadwell 硬件收集的最佳情况吞吐量为每个元素 1.56 个周期。
insertps在 AVX2 之前的架构上,如果不像这样单独加载所有值(使用 SSE4.1或) ,就无法(据我所知)执行此操作pinsrd。
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
Run Code Online (Sandbox Code Playgroud)
对于整数数据,最后一条指令是pinsrd xmm0, [esi+12], 3。
没有 SSE4.1,将movss结果与unpcklps/一起打乱unpcklpd
希望您将其他数据用于某些事情,在这种情况下,加载所有内容并进行转置更有可能是有用的.
如果没有,那么只要数据在向量中有很多工作要做,SIMD根本就是可行的,因为将它打包到向量中是很昂贵的.
movss/ insertps如@ zx485所示,答案是"正常"的方式,就像你使用编译器时可能会得到的那样_mm_set_ps(f[12], f[8], f[4], f[0]);
当你的步幅恰好是4时,使用AVX你可以在两个负载上跨越所有四个浮子并混合.
(相关:什么是最快的步幅-3收集指令序列? 或者对于步幅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 }
Run Code Online (Sandbox Code Playgroud)
这不是很好,因为您可能会与其中一个加载跨越缓存行边界.您可以vshufps ymm0, ymm1, [float8], 0b???????通过第二次加载实现类似的功能.
这可能是好的,具体取决于周围的代码,特别是如果你有AVX2 vpermps(带有一个随机控制矢量常数)或vpermpd(有一个立即)用于车道交叉shuffle将你想要的元素放入低128b通道.
没有AVX2用于跨车道洗牌,你需要vextractf128然后shufps.这可能需要进行一些规划才能将元素放在shufps可以将它们放在正确位置的位置.
当然,这一切都与内在函数一起使用,但它们需要更多的输入.