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

When source registers in avx instruction can be reused

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

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

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

您可以在下一条指令中将寄存器用于不同的目的。与 MIPS 等架构不同,x86 具有互锁流水线阶段,CPU 确保后面的指令不会影响前面的指令。

所有带有 AVX 的 x86 CPUs 通过寄存器重命名来乱序执行以隐藏写后写和读后写 hazards。参见

  • (我的答案顶部附近关于危险和寄存器重命名的部分)

  • 这同样说明这不是问题。

  • What considerations go into predicting latency for operations on modern superscalar processors and how can I calculate them by hand?

  • How many CPU cycles are needed for each assembly instruction? - 依赖链对性能很重要;寄存器重命名后,只有 RAW(写后读)真正的依赖关系很重要。

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

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

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


既然你提到了 gathers,vgatherdps 本身在它的目的地是 not write-only;它根据掩码向量合并。所以如果你在一个循环中重复收集到同一个寄存器(比如 ymm0),你可能想要 vpxor xmm0,xmm0,xmm0 来打破依赖。

但你可能不需要;在 Intel CPUs 上,即使读写目标寄存器尚未“准备好”作为输入,收集元素的实际加载也可以开始。 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 中看到的 link。)

但是,例如,Zen3 上的情况就不是那么好。 Zen 3 has latency from operand 1 -> 1 of 8 cycles 上的 vgatherdps ymm。但这仍然比索引向量就绪 -> 目标向量就绪的 28 个周期延迟短很多。 (2 -> 1)

(对于掩码向量设置为全一的正常收集,您将使用 vpcmpeqd ymm1, ymm1, ymm1。它被认为独立于先前的值,就像异或归零惯用语一样,所以它算作只写,即使您使用的指令看起来像是实际读取和比较。这意味着您已经打破了涉及掩码向量的 dep 链。有趣的是,在 Skylake 上,从掩码输入到输出的周期延迟为 0,如果你 有意避免破坏依赖关系。参见 3->1 section on the uops.info Skylake latency page。大概收集掩码的 vpxor-zeroing 之类的工作,只有在页面错误(或其他错误)时才做不同的事情) 在元素上。)