首页 文章

现代x86成本模型

提问于
浏览
30

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

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

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

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

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

我知道有很多关于x86优化的参考文献(例如迈克尔·阿布拉什),但我有一个预感,比几年前的任何东西都不适用于现代的x86 CPU,因为它们最近发生了很大的变化 . 我对么?

6 回答

  • 3

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

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

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

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

  • 34

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

    编辑

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

  • 1

    值得一提的是,曾经有一本名为"Inner Loops" by Rick Booth的惊人书籍,详细描述了如何为英特尔的80486,奔腾,奔腾Pro和奔腾MMX处理器手动微优化IA-86汇编代码,并提供了大量有用的实用信息 . 世界代码示例(散列,移动内存,随机数生成,霍夫曼和JPEG压缩,矩阵乘法) .

    遗憾的是,自1997年首次出版新版处理器和CPU架构以来,该书一直没有更新过 . 尽管如此,我仍然会建议它作为以下主题的温和介绍:

    • 哪些说明一般非常便宜,或便宜,哪些不是

    • 哪些寄存器是最通用的(即没有特殊含义/不是某些指令的默认寄存器)

    • 如何配对指令,使它们并行执行而不会拖延一个管道

    • 不同种类的摊位

    • 分支预测

    • 关于处理器缓存需要注意什么

  • 1

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

  • 5

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

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

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

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

    • 英特尔®VTune™

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

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

    • 下一代英特尔酷睿微架构

    • Haswell:第四代英特尔酷睿处理器

    • 微操作缓存:可变指令长度ISA的功率感知前端

  • 0

    我正在编写一个带有x86后端的JIT编译器,并且随时学习x86汇编程序和机器代码 .

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

    对于80x86,存在多个具有不同行为/特征的不同CPU . 如果考虑到实际CPU的特定特性,那么进行优化的成本增加了,你直接抨击“成本高于你获得”的障碍 . 对于诸如“理想指令调度”之类的事情尤其如此 .

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

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

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

相关问题