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



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

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

位移索引寄存器是否会在执行指令期间被保留,或者我可以在下一个指令中重用它而不会挂起管道?

所有带有AVX的x86 cpu都使用寄存器重命名来执行乱序执行,以隐藏Write-After-Write和Write-After-Read危险。看到

  • 为什么与Agner's指令表不同,mulss在Haswell上只需要3个周期?(展开带有多个累加器的FP循环)(关于危险和寄存器重命名的部分接近我的答案的顶部)

  • 为英特尔sandybridge系列cpu的管道取消优化程序,类似地解释这不是问题。

  • 预测现代超标量处理器上操作的延迟需要考虑哪些因素?如何手工计算它们?

  • 每条汇编指令需要多少CPU周期?依赖链对性能很重要在寄存器重命名之后,只有RAW(先读后写)真正的依赖关系重要。

您不必担心由于读取或写入前一个值的指令执行缓慢而导致对寄存器的仅写访问延迟。(乱序执行有其限制,物理寄存器文件条目的数量是其中之一,但这是与WAR/WAW危害分开的一个因素。)

寄存器重命名的全部意义在于使对同一个寄存器的新的(独立的)使用像使用不同的寄存器一样,允许CPU利用指令级并行性。

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


既然你提到了集合,vgatherdps本身是而不是只写目标;它根据蒙版向量合并。因此,如果您在一个循环中多次聚集到同一个寄存器中(例如ymm0),您可能需要vpxor xmm0,xmm0,xmm0来打破依赖关系。

但是你可能不需要;在Intel cpu上,即使读写目的寄存器没有"准备好",也可以开始实际加载collect元素。然而作为一种输入。https://uops.info/以1个周期的延迟测量了Skylake上从操作数1到操作数1的延迟。(至少当面具是全一的时候;对于非故障情况,这可能是特殊情况)。

所以vgatherdps ymm0, [rdi+ymm5*4], ymm1可以在ymm0准备就绪后的周期内写入ymm0(如果ymm5ymm1以及指向内存在22个周期前准备就绪)。(集合吞吐量比这更差;他们通过使用像10xvshufpd ymm0, ymm0, ymm0, 0这样的指令链来测量,就像你在那个链接的实验2和3中看到的那样。

然而,事情不是那么好,例如在Zen3上。Zen 3上的vgatherdps ymm从操作数1开始有延迟->8个周期中的1个。但这仍然比从索引向量就绪到28个周期的延迟要短得多。目标矢量准备好了。(2)→1)

(正常聚集的面具向量将宰杀,可以使用vpcmpeqd ymm1, ymm1, ymm1。它被认为是独立于前一个值的,就像xor-zero习惯用法一样,所以它确实被算作只写,即使您使用的指令看起来像是实际在读取和比较。这意味着你已经打破了涉及掩模矢量的深层链。有趣的是,在Skylake上,如果你有意避免破坏依赖,那么从掩码输入到输出的周期延迟为0。请参阅ops.info Skylake延迟页面上的3- 1部分。大概会收集掩码的vpxor调零工作,只有在元素存在页面错误(或其他错误)时才会进行不同的操作。

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

最新更新