估计每条指令的周期
Estimating Cycles Per Instruction
我反汇编了一个使用 MSVC v140 编译的小型 C++ 程序,并试图估计每条指令的周期,以便更好地了解代码设计如何影响性能。我一直在关注 Mike Acton 在 "Data-Oriented Design and C++" 上的 CppCon 2014 演讲,特别是我链接到的部分。
在其中,他指出了这些行:
movss 8(%rbx), %xmm1
movss 12(%rbx), %xmm0
然后他声称这些 2 x 32 位读取可能在同一缓存行上,因此大约花费 ~200 个周期。
Intel 64 and IA-32 Architectures Optimization Reference Manual 是一个很好的资源,特别是 "Appendix C - Instruction Latency and Throughput"。然而,在 "Table C-16. Streaming SIMD Extension Single-precision Floating-point Instructions" 的第 C-15 页上,它 指出 movss 只有 1 个周期 (除非我理解延迟在这里意味着什么错了...如果是这样,我怎么读这个东西?)
我知道 theoretical prediction of execution time 永远不会正确,但是学习这一点很重要。 这两个命令怎么算 200 个周期,我怎样才能学会推理超出这个片段的执行时间?
我已经开始阅读一些关于 CPU 流水线的东西......也许大部分的周期都在那里?
PS:我对在这里实际测量硬件性能计数器不感兴趣。我只是想学习如何合理地阅读 ASM 和循环。
正如您已经指出的,MOVSS 指令的理论吞吐量和延迟为 1 个周期。您正在查看正确的文档(Intel Optimization Manual). Agner Fog (mentioned in the comments) measured the same numbers in his Intruction Tables 英特尔 CPUs(AMD 具有更高的延迟)。
这引出了第一个问题:您正在研究什么具体的微体系结构?这可以产生很大的不同,即使对于同一供应商也是如此。 Agner Fog 报告说,MOVSS 在 AMD Bulldozer 上有 2-6cy 的延迟,具体取决于源和目标(寄存器与内存)。在研究计算机体系结构的性能时,请牢记这一点。
200cy 很可能是缓存未命中,正如评论中已经指出的那样。您从优化手册中获得的任何内存访问指令的数字都是假设数据驻留在一级缓存(L1)中。现在,如果您从未通过之前的指令接触过数据,则需要将高速缓存行(Intel 和 AMD x86 为 64 字节)从内存加载到最后一级缓存,从那里形成二级缓存,然后进入 L1,最后进入 XMM 寄存器(在 1 个周期内)。 L3-L2 和 L2-L1 之间的传输在当前英特尔微体系结构上的每个缓存行具有两个周期的吞吐量(不是延迟!)。内存带宽可用于估算 L3 和内存之间的吞吐量(例如,2 GHz CPU 可实现的内存带宽为 40 GB/s 时,每个缓存行的吞吐量为 3.2 个周期)。缓存行或内存块通常是缓存和内存可以运行的最小单元,它们在微体系结构之间有所不同,甚至在架构内可能不同,具体取决于缓存级别(L1、L2 等)。
现在这是所有吞吐量而不是延迟,这不会帮助您估计上面描述的内容。要验证这一点,您需要一遍又一遍地执行指令(至少 1/10 秒)以获得周期精确测量。通过更改指令,您可以决定是否要测量延迟(通过包括指令之间的依赖性)或吞吐量(通过使指令输入独立于先前指令的结果)。要测量缓存和内存访问,您需要预测访问是否进入缓存,这可以使用 layer conditions.
来完成
估计英特尔 CPUs 指令执行(延迟和吞吐量)的工具是 Intel Architecture Code Analyzer,它支持多种微架构,直至 Haswell。延迟预测要谨慎,因为估计延迟比估计吞吐量要难得多。
我反汇编了一个使用 MSVC v140 编译的小型 C++ 程序,并试图估计每条指令的周期,以便更好地了解代码设计如何影响性能。我一直在关注 Mike Acton 在 "Data-Oriented Design and C++" 上的 CppCon 2014 演讲,特别是我链接到的部分。
在其中,他指出了这些行:
movss 8(%rbx), %xmm1
movss 12(%rbx), %xmm0
然后他声称这些 2 x 32 位读取可能在同一缓存行上,因此大约花费 ~200 个周期。
Intel 64 and IA-32 Architectures Optimization Reference Manual 是一个很好的资源,特别是 "Appendix C - Instruction Latency and Throughput"。然而,在 "Table C-16. Streaming SIMD Extension Single-precision Floating-point Instructions" 的第 C-15 页上,它 指出 movss 只有 1 个周期 (除非我理解延迟在这里意味着什么错了...如果是这样,我怎么读这个东西?)
我知道 theoretical prediction of execution time 永远不会正确,但是学习这一点很重要。 这两个命令怎么算 200 个周期,我怎样才能学会推理超出这个片段的执行时间?
我已经开始阅读一些关于 CPU 流水线的东西......也许大部分的周期都在那里?
PS:我对在这里实际测量硬件性能计数器不感兴趣。我只是想学习如何合理地阅读 ASM 和循环。
正如您已经指出的,MOVSS 指令的理论吞吐量和延迟为 1 个周期。您正在查看正确的文档(Intel Optimization Manual). Agner Fog (mentioned in the comments) measured the same numbers in his Intruction Tables 英特尔 CPUs(AMD 具有更高的延迟)。
这引出了第一个问题:您正在研究什么具体的微体系结构?这可以产生很大的不同,即使对于同一供应商也是如此。 Agner Fog 报告说,MOVSS 在 AMD Bulldozer 上有 2-6cy 的延迟,具体取决于源和目标(寄存器与内存)。在研究计算机体系结构的性能时,请牢记这一点。
200cy 很可能是缓存未命中,正如评论中已经指出的那样。您从优化手册中获得的任何内存访问指令的数字都是假设数据驻留在一级缓存(L1)中。现在,如果您从未通过之前的指令接触过数据,则需要将高速缓存行(Intel 和 AMD x86 为 64 字节)从内存加载到最后一级缓存,从那里形成二级缓存,然后进入 L1,最后进入 XMM 寄存器(在 1 个周期内)。 L3-L2 和 L2-L1 之间的传输在当前英特尔微体系结构上的每个缓存行具有两个周期的吞吐量(不是延迟!)。内存带宽可用于估算 L3 和内存之间的吞吐量(例如,2 GHz CPU 可实现的内存带宽为 40 GB/s 时,每个缓存行的吞吐量为 3.2 个周期)。缓存行或内存块通常是缓存和内存可以运行的最小单元,它们在微体系结构之间有所不同,甚至在架构内可能不同,具体取决于缓存级别(L1、L2 等)。
现在这是所有吞吐量而不是延迟,这不会帮助您估计上面描述的内容。要验证这一点,您需要一遍又一遍地执行指令(至少 1/10 秒)以获得周期精确测量。通过更改指令,您可以决定是否要测量延迟(通过包括指令之间的依赖性)或吞吐量(通过使指令输入独立于先前指令的结果)。要测量缓存和内存访问,您需要预测访问是否进入缓存,这可以使用 layer conditions.
来完成估计英特尔 CPUs 指令执行(延迟和吞吐量)的工具是 Intel Architecture Code Analyzer,它支持多种微架构,直至 Haswell。延迟预测要谨慎,因为估计延迟比估计吞吐量要难得多。