X86 预取优化:“计算 goto”线程代码

2024-05-14

我有一个相当重要的问题,我的计算图有循环和多个“计算路径”。我没有制作一个调度程序循环,其中每个顶点将被一一调用,而是将所有预先分配的“框架对象”放置在堆中(代码+数据)。
这有点类似于线程代码(甚至更好:CPS),只是在堆中跳转,执行代码。每个代码段都与堆中自己的“帧指针”相关联,并使用与之相关的数据。帧始终保持分配状态。该代码只是在已知位置产生副作用,计算(如果需要)下一个转到值并跳转到那里。
我还没有尝试过(这将是使其正确的一项重大任务,我完全意识到所有的困难)所以我想问 x86 机器上的专家:它可以比调度程序循环更快吗?我知道硬件中对 call/ret 指令进行了多种优化。
相对于堆栈指针或任何其他指针访问数据之间有区别吗?是否存在间接跳转的预取(跳转到寄存器中存储的值?)。
这个想法可行吗?

附:如果你读过这篇文章但仍然无法理解我的意思(请原谅我未能解释事情的尝试),请将这整个想象为一组预先分配的堆上的协程彼此屈服。该过程中不使用标准 x86 堆栈,因为所有内容都在堆上。


与返回一个父间接分支相比,直接从一个块跳转到另一个块通常是分支预测的胜利,尤其是在早于 Intel Haswell 的 CPU 上。


通过从每个块的尾部跳转,每个分支都有不同的分支预测器历史。给定块通常跳转到相同的下一个块,或者具有几个目标地址的简单模式,这可能是常见的。这通常可以很好地预测,因为每个分支都有一个更简单的模式,并且分支历史分布在多个分支上。

如果所有调度都发生在单个间接分支上,则可能只有一个 BTB(分支目标缓冲区)条目,并且该模式将过于复杂而无法很好地预测。

Intel Haswell 中的现代 TAGE 分支预测器以及后来使用最近的分支历史记录(包括间接分支目标)对 BTB 进行索引,这实际上解决了这个问题。见评论X86 64 位模式上的索引分支开销 https://stackoverflow.com/questions/47052536/indexed-branch-overhead-on-x86-64-bit-mode#comment81079172_47052536,然后在中搜索 Haswellhttps://danluu.com/branch-prediction/ https://danluu.com/branch-prediction/。一个分支的复杂模式可能会将其预测分散到许多 BTB 条目中。

具体来说,分支预测和解释器的性能 - 不要相信民间传说 https://hal.inria.fr/hal-01100647/document (2015)Rohou、Swamy 和 Seznec 在解释器基准上对 Nehalem、SandyBridge 和 Haswell 进行了比较,并测量了单个调度循环的实际错误预测率switch陈述。他们发现 Haswell 做得更好,可能使用了 ITTAGE 预测器。

他们不测试 AMD CPU。自从 Piledriver 使用 Piledriver 以来,AMD 已经发布了一些有关其 CPU 的信息用于分支预测的感知器神经网络 https://en.wikipedia.org/wiki/Branch_predictor#Neural_branch_prediction。我不知道他们用单个间接分支处理调度循环的效果如何。 (AMD 自 Zen 2 使用 IT-TAGE 作为二级分支预测器 https://en.wikichip.org/wiki/amd/microarchitectures/zen_2#Branch_Prediction_Unit,除了他们从 Zen 1 中保留的散列感知器之外。)


达里克·米霍卡讨论这个模式 http://www.emulators.com/docs/nx25_nostradamus.htm在解释 CPU 仿真器的上下文中,它针对不同的指令(或简化的微指令)从一个处理程序块跳转到另一个处理程序块。他详细介绍了各种策略在 Core2、Pentium4 和 AMD Phenom 上的性能。 (写于2008年)。当前 CPU 上的现代分支预测器最类似于 Core2。

他最终提出了他所谓的诺查丹玛斯分配器模式,以分支预测友好的方式检查提前退出(函数返回函数指针,或“防火通道”哨兵)。如果您不需要,请参阅文章的前半部分,其中他讨论了块之间的直接跳转链接与中央分发器。

他甚至抱怨 x86 中缺少代码预取指令。对于 Pentium 4,这可能是一个更大的问题,其中填充跟踪缓存的初始解码是very与从跟踪缓存运行相比,速度较慢。 Sandybridge 系列有一个解码的 uop 缓存,但它不是跟踪缓存,并且解码器仍然足够强大,不会在 uop 缓存未命中时出现问题。锐龙也类似。

相对于堆栈指针或任何其他指针访问数据之间有区别吗?

不,你甚至可以设置rsp跳转后,每个块都可以有自己的堆栈。如果您安装了任何信号处理程序,rsp需要指向有效的内存。另外,如果您希望能够call任何普通的库函数,你需要rsp用作堆栈指针,因为他们想要ret.

是否存在间接跳转的预取(跳转到寄存器中存储的值?)。

预取到 L2 可能很有用如果您在准备好执行间接跳转之前就知道分支目标地址。当前所有 x86 CPU 都使用分离式 L1I / L1D 缓存,因此prefetcht0会污染L1D而没有任何增益,但是prefetcht1 http://felixcloutier.com/x86/PREFETCHh.html可能有用(提取到 L2 和 L3)。或者,如果代码在 L2 中已经很热门,那么它可能根本没有用。

也很有用:尽早计算跳转目标地址,因此乱序执行可以解决分支,同时大量工作在乱序核心中排队。这可以最大限度地减少管道中潜在的气泡。如果可能的话,保持计算独立于其他内容。

最好的情况是在寄存器中寻址许多指令之前jmp,所以只要jmp在执行端口上获取一个周期,它可以向前端提供正确的目的地(如果分支预测出错则重新引导)。最坏的情况是分支目标是分支之前指令的长依赖链的结果。几个独立的指令和/或内存间接跳转就可以了;一旦这些指令进入 OOO 调度程序,乱序执行就应该找到运行这些指令的周期。

还有分离的 L1iTLB 和 L1dTLB,但 L2TLB 在大多数微架构上通常是统一的。但是 IIRC,L2TLB 充当 L1 TLB 的受害者缓存。预取可能会触发页面遍历以填充 L1 数据 TLB 中的条目,但在某些微体系结构上这无助于避免 iTLB 缺失。 (至少它会将页表数据本身放入 L1D 中,或者可能是页遍历硬件中的内部页目录缓存,因此同一条目的另一个页遍历会很快。但是由于 Intel Skylake(及更高版本)以外的 CPU只有 1 个硬件页面遍历单元,如果在第一页遍历仍在发生时发生 iTLB 未命中,它可能无法立即启动,因此如果您的代码如此分散以至于出现 iTLB 未命中,实际上可能会造成伤害.)

对要 JIT 的内存块使用 2MB 大页面,以减少 TLB 未命中。可能最好将代码放置在相当紧凑的区域中,并将数据分开。 DRAM 局部效应是真实存在的。 (我认为 DRAM 页面通常大于 4kiB,但这是硬件问题,您无法选择。在已打开的页面中访问的延迟较低。)

See 阿格纳·福格的微建筑 pdf http://agner.org/optimize/,并且英特尔的优化手册。 https://software.intel.com/en-us/articles/intel-sdm#optimization。 (如果您担心 AMD CPU,还有 AMD 的手册)。查看更多链接x86 /questions/tagged/x86标签维基。

这个想法可行吗?

应该是。

如果可能,当一个块总是跳转到另一个块时,请通过使块连续来消除跳转。

数据的相对寻址很简单:x86-64 具有 RIP 相对寻址。

You can lea rdi, [rel some_label]然后从那里建立索引,或者直接对某些静态数据使用 RIP 相对寻址。

您将对代码或其他内容进行 JIT 操作,因此只需计算从当前指令末尾到要访问的数据的有符号偏移量,这就是 RIP 相对偏移量。位置无关代码 + 静态数据在 x86-64 中很容易。


在花岗岩急流及之后, PREFETCHIT0 [rip+rel32]将代码预取到“所有级别”的缓存中,或者prefetchit1预取到除 L1i 之外的所有级别。

这些指令是 NOP,其寻址模式不是 RIP 相关的,或者在不支持它们的 CPU 上。 (也许他们还启动 iTLB 甚至 uop 缓存,或者至少在纸面上可以。)截至 2022 年 12 月的英特尔“未来扩展”手册中的文档建议目标地址是某些指令的开头。

仅当您足够早地进行预取时,预取才有用,并且它不能解决错误预测问题。对于解释器来说,预取字节码指令的代码可能是也可能不是胜利after当前的一个。prefetchit0不能这样做,它只适用于 RIP 相对寻址,不适用间接寻址。也许是因为 CPU 的代码获取部分(如 L1i 和 iTLB)没有用于任意地址的 AGU,如果它通过将地址提供给这些部分来工作?因此,它对于预取运行时变量代码位置没有帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

X86 预取优化:“计算 goto”线程代码 的相关文章

随机推荐

  • iostream 对象 cin、cout、cerr 和 clog 是如何实现的?

    iostream 对象 cin cout cerr 和 clog 是在 iostream 标头中声明的对象 我知道在某些编译器中可能会在构造这些 iostream 对象之前尝试使用它们 因此在某些情况下它们必须受到 静态初始化顺序失败 的影
  • 查找 postgres 提供的列表中不存在的值

    我试图找到一个查询来告诉我数据库中没有的值 例如 select seqID segment from flu where seqID IN 1 2 3 4 5 6 7 8 9 现在 如果我的数据库没有 seqID 3 8 9 我将如何查找
  • LINQ:从类型 T 的列表中,仅检索某个子类 S 的对象

    给定一个简单的继承层次结构 人 gt 学生 教师 职员 假设我有一个人员列表 L 该列表中有一些学生 教师和工作人员 使用 LINQ 和 C 有没有办法编写一个只能检索特定类型人员的方法 我知道我可以做类似的事情 var peopleIWa
  • 如何使 Rails 3 资源预编译速度更快?

    我有一个正在运行的 Rails 3 2 1 应用程序 我正在通过 Capistrano 进行部署deploy assets它运行deploy assets precompile task 一切工作都很好 除了编译本身非常慢 我没有那么多 C
  • 发布到 npm 时出现问题

    所以我尝试使用 React 构建一个开源项目并将其推送到 npm 问题是 我的组件虽然在测试环境中运行良好 安装到其他组件 但当我将其发布到 npm 并下载包并尝试访问它时 它给了我一个错误 这是代码的一个小示例 import React
  • 在 C# 中生成随机浮点数的最佳方法[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 在 C 中生成随机浮点数的最佳方法是什么 更新 我想要从 float Minvalue 到 float Maxvalue 的随机浮点数 我在一些
  • 发布数据时维护 ViewBag 值

    我有一个逻辑问题需要回答 这是一个场景 在控制器中 ViewBag Name aaaa 在视野中 ViewBag Name 在我的控制器中 我为 ViewBag 设置了值 并从 VIew 中的 ViewBag 检索了值 现在在 View 中
  • ASP.NET Core 过滤器内的 ViewBag

    基于这篇文章制作一个全局ViewBag https stackoverflow com questions 23381295 make a global viewbag 我正在尝试在 ASP NET Core 2 2 中实现以下内容 我意识
  • 为什么在 10.5 上我必须在 NSWindowController 上调用 showWindow 两次?

    我有一个 NSWindowController 的子类 我用它从笔尖加载窗口并将其显示在屏幕上 下面是当我想显示窗口时调用的代码 在 10 6 上 当调用 showCustomWindow 时 会显示窗口 但在 10 5 上 必须调用此方法
  • 以编程方式在指令内添加指令

    我想将指令的另一个实例附加到父指令中 但我无法使用 apply 重新编译我的指令 我想我在某个地方错过了一些东西 我的 HTML 代码 div div div div
  • 关闭文件后未保存展开/折叠状态

    在 Visual Studio 中 您可以在不使用区域的情况下展开和折叠代码 例如在代码隐藏页面中您可以折叠方法等 而在 ASPX 页面中您可以折叠标签 标签等 当您的页面很长并且您希望将开发重点放在特定部分时 它非常有用 它的一个很酷的地
  • 如何在 Angular 单元测试中模拟/触发 $routeChangeSuccess?

    给定一个附加到 routeChangeSuccess 事件的处理程序来更新 rootScope 上的 title 属性 rootScope on routeChangeSuccess function event current previ
  • 将密钥对添加到现有 EC2 实例

    我被给予AWS控制台访问正在运行 2 个实例的帐户 但我无法关闭 在生产中 但是 我想获得对这些实例的 SSH 访问权限 是否可以创建一个新的密钥对并将其应用到实例 以便我可以通过 SSH 访问 获取现有的pem当前无法选择创建实例所用的密
  • 在 XSD 中哪里放置版权信息?

    将版权信息放入 XML 架构定义 XSD 时 是否有官方 或半官方 普遍接受的 位置 基于在哪里将版本添加到 XSD 架构 https stackoverflow com questions 2138705 where to add a v
  • setImageCompressionQuality 与 setCompressionQuality 之间有什么区别 - Imagick

    我在Imagick中找到了两种设置图像压缩质量的方法 A 设置图像压缩质量 B 设置压缩质量 所以我想知道哪一个是最好的以及为什么在以下条件下 我读到了setCompressionQuality方法仅适用于新图像 我正在尝试压缩文件 jpe
  • 自动测试无限循环

    我在 Rails 项目中使用自动测试时遇到了一个问题 即当测试失败时 即 我修改了测试文件并且自动测试自动运行测试 自动测试会不断尝试运行测试 当然每次都会失败 因为文件尚未被修改 而不是等到文件再次保存 当咆哮通知打开时 当我试图修复代码
  • 无法在我的程序中使用 matplotlib 函数

    我正在 Windows 10 中运行 Anaconda 安装 conda 版本 4 3 8 这是我尝试在 python 命令行中运行的代码 import matplotlib pyplot as plt x 1 2 3 4 y 5 6 7
  • JAXB - 忽略元素

    有什么方法可以忽略 Jaxb 解析中的元素吗 我有一个很大的 XML 文件 如果我可以忽略其中一个大而复杂的元素 那么它的解析速度可能会快很多 如果它根本无法验证元素内容并解析文档的其余部分 即使该元素不正确 那就更好了 例如 这应该只生成
  • 为什么 C++11 的移动构造函数/赋值运算符没有按预期运行

    include
  • X86 预取优化:“计算 goto”线程代码

    我有一个相当重要的问题 我的计算图有循环和多个 计算路径 我没有制作一个调度程序循环 其中每个顶点将被一一调用 而是将所有预先分配的 框架对象 放置在堆中 代码 数据 这有点类似于线程代码 甚至更好 CPS 只是在堆中跳转 执行代码 每个代