assembly - avx指令中的源寄存器何时可以重用

标签 assembly cpu-architecture simd avx micro-optimization

在avx指令中用作源的寄存器在指令开始处理后何时可以重用?

例如:我想使用 vgatherdps 指令,它消耗两个 ymm 寄存器,其中一个是位移索引。我意识到 vgatherdps 需要大量时间来收集数据,因为数据的局部性很差。

在指令执行期间是否保留位移索引寄存器,或者我可以在后续指令中重复使用它而不挂起流水线?

最佳答案

所有带有 AVX 的 x86 CPU 都会乱序执行,并通过重命名寄存器来隐藏 Write-After-Write 和 Write-After-Read hazards .见

您永远不必担心由于执行读取或写入前一个值的缓慢指令而导致对寄存器的只写访问停止。(乱序执行有其限制,和 number of physical register-file entries 是其中之一,但这是与 WAR/WAW 危害不同的因素。)

寄存器重命名的全部意义在于使对同一寄存器的新(独立)使用执行起来就像使用不同的寄存器一样,从而允许 CPU exploit the instruction-level parallelism .

例如,vmovdqa ymm2, [rdi] 不关心之前的指令读取或写入 ymm2(或其 xmm2 低半部分); vmovdqa 的目的地始终是只写的。


既然你提到了聚会,vgatherdps它本身在其目的地不是只写的;它根据掩码向量进行合并。因此,如果您在循环中重复收集到同一个寄存器(例如 ymm0),您可能需要 vpxor xmm0,xm​​m0,xm​​m0 来打破依赖关系。

但您可能不需要;在 Intel CPU 上,即使读写目标寄存器还没有“准备好”作为输入,收集元素的实际负载也可以开始。 https://uops.info/ measured Skylake 上从操作数 1 到操作数 1 的延迟为 1 个周期延迟。 (至少当掩码为全一时;对于非故障情况,这可能是特殊情况)。

所以 vgatherdps ymm0, [rdi+ymm5*4], ymm1 可以在 ymm0 准备好后的循环中写入 ymm0 (如果 ymm5ymm1,以及指向的内存,在 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/

相关文章:

c - 使用结构时,如何将以下汇编代码从编译器翻译成 C?

c - 缓冲区溢出(返回地址)

x86 - 驱动程序将如何使用MONITOR指令(_mm_monitor内部函数)?

mips - 如何在 icarus verilog 中包含文件?

SSE2 : How to reduce a _m128 to a word

c - 如何访问 C 中 .size 指令设置的符号大小

assembly - x86 asm指令集: Any _searchable_ offline reference?

cpu - 如何使用(读/写)CPU 缓存 L1、L2、L3

c++ - 矢量化和#pragma omp simd

assembly - 如何将二进制整数转换为十六进制字符串?