现代x86成本模型

时间:2012-03-31 15:01:55

标签: performance assembly x86 floating-point micro-optimization

我正在编写一个带有x86后端的JIT编译器,并且随时学习x86汇编程序和机器代码。我大约20年前使用ARM汇编程序,并对这些体系结构之间的成本模型差异感到惊讶。

具体来说,内存访问和分支在ARM上很昂贵,但在x86上等效的堆栈操作和跳转很便宜。我相信现代x86 CPU比ARM内核做更多的动态优化,我发现很难预测它们的影响。

编写x86汇编程序时要记住什么是好的成本模型?哪些指令组合便宜又昂贵?

例如,如果它总是生成用于加载整数或跳转到偏移的长格式,即使整数很小或偏移量接近但这会影响性能,我的编译器会更简单吗?

我还没有做任何浮动点,但我很快就会接受它。普通代码和浮动代码之间的相互作用有什么不明显的吗?

我知道有很多关于x86优化的参考文献(例如Michael Abrash),但是我有一个预感,而不是几年前的任何东西都不适用于现代的x86 CPU,因为它们最近发生了很大的变化。我是对的吗?

6 个答案:

答案 0 :(得分:35)

最佳参考是Intel Optimization Manual,它提供了有关所有最新英特尔内核的架构危险和指令延迟的相当详细的信息,以及大量优化示例。

另一个很好的参考是Agner Fog's optimization resources,它具有覆盖AMD核心的优点。

请注意,具体的成本模型本质上是微架构特定的。没有“x86成本模型”这样的东西具有任何真实的有效性。在指令级别,Atom的性能特征与i7完全不同。

我还要注意,内存访问和分支在x86内核上实际上并不“便宜” - 只是无序执行模型变得如此复杂以至于它可以成功地隐藏它们的成本很多简单场景。

答案 1 :(得分:5)

TorbjörnGranlund的Instruction latencies and throughput for AMD and Intel x86 processors也很好。

修改

Granlund的文档涉及在每个时钟周期可以发出特定类型的指令的情况下的指令吞吐量(我是并行执行的)。他还声称英特尔的文档并不总是准确的。

答案 2 :(得分:3)

对于它的价值,曾经有一本名为"Inner Loops" by Rick Booth惊人的书,详细描述了如何手动微量优化英特尔80486奔腾的IA-86汇编代码, Pentium Pro和Pentium MMX处理器,包含许多有用的实际代码示例(散列,移动内存,随机数生成,Huffman和JPEG压缩,矩阵乘法)。

不幸的是,自从1997年首次出版新的处理器和CPU架构以来,这本书一直没有更新。尽管如此,我仍然会建议它作为对以下主题的温和介绍:

  • 哪些说明通常非常便宜,或便宜,而且不是
  • 哪些寄存器是最通用的(即没有特殊含义/不是某些指令的默认寄存器)
  • 如何配对指令,使它们并行执行而不会拖延一个管道
  • 不同种类的摊位
  • 分支预测
  • 关于处理器缓存的注意事项

答案 3 :(得分:1)

值得关注的是后端现有的开源编译器,如GCC和LLVM。它们具有指令成本模型以及体面(但理想化)的机器模型(例如,发布宽度,高速缓存大小等)。

答案 4 :(得分:1)

当然,Agner Fog的报告以及英特尔®64和IA-32架构优化参考手册都是必不可少的优秀参考资料。 AMD还有一本优化手册:

  • AMD系列15h处理器的软件优化指南

但是,两个英特尔工具对于理解代码序列至关重要:

  • 英特尔®架构代码分析器
  • 英特尔®VTune™

IACA是您的成本模式。我在OSX上使用它,但VTune只能在Windows和Linux上运行。

您还可以深入了解英特尔专利文献和各种英特尔论文,以便更好地了解工作原理:

  • 下一代英特尔酷睿微体系架构
  • Haswell:第四代英特尔酷睿处理器
  • 微操作缓存:可变指令长度ISA的功率感知前端

答案 5 :(得分:0)

  

我正在编写一个带有x86后端和学习x86的JIT编译器   我去的汇编程序和机器代码。

这里的基本问题是JIT编译器不能花费大量时间进行微优化。由于“优化”在运行时发生,因此优化的成本需要小于优化所节省的时间(否则优化会成为性能的净损失)。

对于80x86,有多个不同的CPU具有不同的行为/特征。如果考虑到实际CPU的特定特性,那么进行优化的成本会增加,并且您会直接陷入“成本高于获利”的障碍。对于诸如“理想指令调度”之类的事情尤其如此。

幸运的是,大多数(但不是全部)现代80x86 CPU具有各种功能(无序,推测执行,超线程),以减轻(某些)由“不完美”优化引起的性能成本。这往往会使昂贵的优化效果降低。

您要做的第一件事是确定应该优化哪些代码片段以及哪些代码片段不应该优化。不经常执行的事情(例如,“仅执行一次”初始化代码)根本不应该被优化。它只是经常被执行的部分(例如内环等)值得打扰。一旦你确定了一件值得优化问题的作品,那就变成了“多少钱?”。

粗略的过度概括;我希望(平均而言)90%的代码根本不值得优化,而对于9%的代码,它只值得做一些通用优化。剩余的1%(可能受益于理论上的广泛优化)最终会让JIT编译器开发人员在实践中烦恼太多(并且会导致大量复杂性/可验证性的噩梦 - 例如“只有在存在时才存在的错误”在一些CPU“场景”上运行。