我已经拆解了一个用MSVC v140编译的小型C ++程序,并试图估算每条指令的周期,以便更好地理解代码设计如何影响性能。我一直关注Mike Acton的CppCon 2014关于"Data-Oriented Design and C++"的演讲,特别是我与之相关的部分。
在其中,他指出了以下几点:
movss 8(%rbx), %xmm1
movss 12(%rbx), %xmm0
然后他声称这些 2 x 32位读取可能在同一个缓存行上,因此大约需要200个周期。
Intel 64 and IA-32 Architectures Optimization Reference Manual是一个很好的资源,特别是"附录C - 指令延迟和吞吐量" 。但是在"表C-16中的第C-15页上。流式SIMD扩展单精度浮点指令" 它表明movss只有1个周期(除非我理解延迟意味着什么错误...如果是这样的话,我怎么读这个东西?)
我知道theoretical prediction of execution time永远不会是正确的,但不过这一点很重要。 这两个命令如何200个循环,以及如何学习推理超出此代码段的执行时间?
我已经开始阅读有关CPU流水线的一些内容......也许大部分周期都在那里被选中了?
PS:我对这里实际测量硬件性能计数器不感兴趣。我只是想学习如何合理地观察ASM和周期。
答案 0 :(得分:2)
正如您已经指出的那样,MOVSS指令的理论吞吐量和延迟是1个周期。您正在查看正确的文档(Intel Optimization Manual)。 Agner Fog(在评论中提到)测量了Intruction Tables中针对Intel CPU的相同数字(AMD具有更高的延迟)。
这引出了第一个问题:您正在研究哪些具体的微体系结构?这可以产生很大的不同,即使对于同一个供应商也是如此。 Agner Fog报道,根据源和目的地(寄存器与内存),MOVSS在AMD Bulldozer上的延迟为2-6秒。在研究计算机体系结构的性能时,请务必牢记这一点。
200cy很可能是缓存未命中,正如已经指出的那样,在评论中也存在。对于任何内存访问指令,您从优化手册中获得的数字都假设数据位于第一级缓存(L1)中。现在,如果您从未通过之前的指令触及数据,则需要将缓存行(带有Intel和AMD x86的64字节)从内存加载到最后一级缓存,从那里进入二级缓存,然后进入L1,最后进入XMM寄存器(1个周期内)。 L3-L2和L2-L1之间的传输在当前的英特尔微体系结构上具有每个缓存线两个周期的吞吐量(不是延迟!)。并且存储器带宽可以用于估计L3和存储器之间的吞吐量(例如,具有40GB / s的可实现存储器带宽的2GHz CPU将具有每个高速缓存线3.2个周期的吞吐量)。高速缓存行或内存块通常是最小的单元高速缓存和内存可以操作,它们在微体系结构之间有所不同,甚至可能在体系结构中有所不同,具体取决于高速缓存级别(L1,L2等)。
现在这是所有吞吐量而不是延迟,这无法帮助您估算上述内容。要验证这一点,您需要反复执行指令(至少1 / 10s)以获得周期精确测量。通过更改指令,您可以决定是否要测量延迟(通过包括指令之间的依赖关系)或吞吐量(通过使指令输入独立于先前指令的结果)。要测量缓存和内存访问,您需要预测访问是否要进入缓存,这可以使用layer conditions来完成。
估算英特尔CPU指令执行(延迟和吞吐量)的工具是Intel Architecture Code Analyzer,它支持Haswell之前的多个微体系结构。延迟预测将采用一粒盐,因为估计延迟比吞吐量要困难得多。