在avx指令中用作源的寄存器在指令开始处理后何时可以重用?
例如:我想使用 vgatherdps
指令,它消耗两个 ymm 寄存器,其中一个是位移索引。我意识到 vgatherdps
需要大量时间来收集数据,因为数据的局部性很差。
在指令执行期间是否保留位移索引寄存器,或者我可以在后续指令中重复使用它而不挂起流水线?
最佳答案
所有带有 AVX 的 x86 CPU 都会乱序执行,并通过重命名寄存器来隐藏 Write-After-Write 和 Write-After-Read hazards .见
Why does mulss take only 3 cycles on Haswell, different from Agner's instruction tables? (Unrolling FP loops with multiple accumulators) (我的答案顶部附近关于危险和寄存器重命名的部分)
Deoptimizing a program for the pipeline in Intel Sandybridge-family CPUs这同样解释了这不是问题。
How many CPU cycles are needed for each assembly instruction? - 依赖链对性能很重要;寄存器重命名后,只有 RAW(写后读)真正的依赖关系很重要。
您永远不必担心由于执行读取或写入前一个值的缓慢指令而导致对寄存器的只写访问停止。(乱序执行有其限制,和 number of physical register-file entries 是其中之一,但这是与 WAR/WAW 危害不同的因素。)
寄存器重命名的全部意义在于使对同一寄存器的新(独立)使用执行起来就像使用不同的寄存器一样,从而允许 CPU exploit the instruction-level parallelism .
例如,vmovdqa ymm2, [rdi]
不关心之前的指令读取或写入 ymm2(或其 xmm2 低半部分); vmovdqa
的目的地始终是只写的。
既然你提到了聚会,vgatherdps
它本身在其目的地不是只写的;它根据掩码向量进行合并。因此,如果您在循环中重复收集到同一个寄存器(例如 ymm0
),您可能需要 vpxor xmm0,xmm0,xmm0
来打破依赖关系。
但您可能不需要;在 Intel CPU 上,即使读写目标寄存器还没有“准备好”作为输入,收集元素的实际负载也可以开始。 https://uops.info/ measured Skylake 上从操作数 1 到操作数 1 的延迟为 1 个周期延迟。 (至少当掩码为全一时;对于非故障情况,这可能是特殊情况)。
所以 vgatherdps ymm0, [rdi+ymm5*4], ymm1
可以在 ymm0
准备好后的循环中写入 ymm0
(如果 ymm5
和 ymm1
,以及指向的内存,在 22 个周期之前就准备好了)。 (收集吞吐量比这更糟糕;他们通过使用像 10x vshufpd ymm0, ymm0, ymm0, 0
这样的指令链来衡量这一点,正如您在该链接中的实验 2 和 3 中看到的那样。)
但是,例如,Zen3 的情况就不那么好了。 vgatherdps ymm 上 Zen 3 has latency from operand 1 -> 1 of 8 cycles .但这仍然比索引向量就绪 -> 目标向量就绪的 28 个周期延迟要短得多。 (2 -> 1)
(对于掩码向量设置为全一的正常聚集,您将使用 vpcmpeqd ymm1, ymm1, ymm1
。它被识别为独立于之前的值,就像异或归零习语一样, 所以它确实算作只写,即使您使用的指令看起来像它实际上会读取和比较。这意味着您已经破坏了涉及掩码向量的 dep 链。有趣的是在 Skylake 上,有 0 个周期延迟如果您确实有意避免破坏依赖关系,则从掩码输入到输出。请参阅 3->1 section on the uops.info Skylake latency page。大概会收集掩码的 vpxor-zeroing 之类的工作,只有在出现页面错误(或其他错误)在一个元素上。)
关于assembly - avx指令中的源寄存器何时可以重用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69492112/