关注了就能看到更多这么棒的文章哦~
Kernel optimization with BOLT
By Jake Edge
October 25, 2024
LPC
Gemini-1.5-flash translation
https://lwn.net/Articles/993828/
2024 年 Linux Plumbers Conference 的 toolchains track 中的两场演讲探讨了不同的内核优化工具。首先,Maksim Panchenko 介绍了 Meta 在其生产内核中使用的 binary optimization and layout tool(BOLT,二进制优化和布局工具)。BOLT 通过重新排列内核二进制文件来改进代码局部性 (code locality),从而提高性能。还有一篇 后续文章 将会介绍第二场演讲,该演讲探讨了 automatic feedback-directed optimization(AutoFDO,自动反馈导向优化)以及 Google 用于优化其内核的其他相关技术。
Panchenko 首先展示了一张幻灯片,其中列出了使用 BOLT 的一些公司和项目,这些内容可以在他的 幻灯片 或 YouTube 视频 中看到。BOLT 最初是为 Meta 的大型应用程序设计的,但后来发现它也可以加速编译器。例如,从 3.12 版本开始,Python 就使用了它;LLVM、Rust 和其他一些项目也使用了它。
他表示,如果回顾十年前的开源世界,要从应用程序中获得最大性能,就要使用 GCC 或 Clang,并结合 -O3 、profile-guided optimization(PGO,使用配置文件引导的优化)和 link-time optimization(LTO,链接时优化)。但是应用 PGO “有点痛苦”,所以只有那些非常关心性能的人才会使用它。将 PGO 应用于内核更加痛苦,因此大多数公司只是在其内核上使用 -O2 或 -O3 。
Meta 的主要应用程序是一个运行 PHP 代码的虚拟机;大约在 2015 年,这里的开发人员提出了一个二进制优化器的想法,该优化器可以与 GCC 和 Clang 一起工作,以加速生成的代码。BOLT 的效果超出了开发人员的预期;它带来了巨大的收益,并且能够加速编译器本身。自 2016 年以来,Meta 一直在使用它;在整个 Meta 的机群中,“大多数周期都花在了 BOLT 优化的二进制文件上”。BOLT 于 2018 年以 Apache 2.0 许可证开源发布,并自 2022 年起成为 LLVM 的一部分。

通常,开发人员会考虑如何在内存中排列程序的数据结构,但是他们很少考虑代码是如何排列的。但也有一些例外,包括 Linux 内核开发人员,但大多数时候,重点都放在数据缓存上。指令缓存比数据缓存小得多,而且随着时间的推移并没有增长多少,在过去 20 年里,英特尔 CPU 上的指令缓存可能从 32KB 翻倍到 64KB。但是,对于那些不将大部分时间花在紧密循环中的大型应用程序来说,代码在内存中的布局非常重要,因此 BOLT 可以带来很大的改变,他说道。
即使有了 profiling 信息,编译器也没有足够的信息来优化代码布局;事实证明,内联函数会改变 profile 结果。profiling 信息可能指向一个被频繁调用的函数 foo() ,但是当它被内联时,该信息就不存在了。BOLT 对二进制文件进行操作,以观察频繁执行的代码,以便将所有这些代码放在内存中彼此靠近的位置。他表示,自从 2019 年 BOLT 论文 发布以来,其他一些工作,包括 Propeller,也应运而生;然而,它们通常与具体的某个工具链绑定,而 BOLT 可以与 GCC 或 Clang 以及不同的链接器一起使用。
他展示了 HHVM 运行时内存布局的一种热力图,HHVM 是 Meta 用于其大部分工作负载的运行时(runtime)。一张图片显示热点代码分散在各处,而 BOLT 优化后的图片显示所有热代码都集中在内存的同一小区域内。这大大减少了指令缓存未命中、转换后备缓冲区 (TLB) 未命中和 CPU 时间;Panchenko 说,对于 HHVM,BOLT 使性能提高了 7%。
BOLT 是一个链接后的优化器,它针对 ELF 二进制文件进行操作,比如 vmlinux 就是一个例子。即使它是 LLVM 项目的一部分,BOLT 仍然支持 GCC 代码;它还支持“最流行”的架构:x86_64、Arm64 和 RISC-V。
将 BOLT 应用于内核花费了一些时间,主要是为了确保生成的内核能够运行并且不会崩溃。遇到的一个主要问题是找到一个好的基准测试程序来衡量 BOLT 的影响。有很多不同的微基准测试程序可用,但“我找不到任何可扩展的大规模基准测试程序”。最后,他使用了 RocksDB db_bench fillseq 基准测试程序,该程序仅通过切换到 BOLT 优化的内核就显示出 2.5% 的改进。他和 Meta 的其他人在一个 BOLT 优化的内核上运行了公司的一项主要服务,每秒查询数 (QPS) 提高了 2%,“这是相当显著的”。
BOLT 仅更改分支和二进制文件中 基本块 的位置来实现其改进。大多数情况下,为了应用 BOLT,不需要重新编译应用程序;但使用 split functions 构建的程序除外,BOLT 可以比编译器做得更好。应用程序确实需要重新链接才能生成重定位信息。有了这些信息,以及内核运行代表性工作负载的配置文件,BOLT 只需大约四秒钟即可优化 vmlinux ,他说道。
可以使用多种机制生成配置文件,包括英特尔平台上的最后分支记录 (LBR) 功能和其他架构上类似的分支采样功能。如果这些功能不可用,则可以通过检测代码来收集所需的信息,但这比使用 LBR 等方法的开销更大。还有其他选项,但配置文件质量以及 BOLT 优化效果不会那么好。
Panchenko 说,BOLT 需要一个未 strip 过的 vmlinux 二进制文件,因为它使用符号名称来发现代码。BOLT 可以使用定义的文本段和数据段轻松识别 ELF 二进制文件中代码和数据的边界。这些段进一步划分为节,BOLT 使用符号表信息来识别其中的各个函数。
然后 BOLT 反汇编这些函数,但是与 objdump 不同的是,它会“符号化指令的操作数”。然而,区分指令中的常量和地址可能是一个问题。链接器插入的重定位信息对此有所帮助;它可以用来“有效地进行代码的符号反汇编”。
可以使用常规技术(例如 peephole 优化)来优化生成的指令流,但“这不是很有效”。为了对代码进行更多优化,需要某种中间表示 (IR)。Panchenko 说,使用的 IR“本质上是 MC 指令之上的控制流图”;他指的是 LLVM 机器码 (MC) 项目,该项目提供了 BOLT 使用的许多工具和库。这些指令看起来很像汇编代码,但是有些指令可能会被注释或修改,例如,用于区分尾调用(tail call)和其他类型的跳转。“因此,如果你查看 BOLT 反汇编,你会比常规 objdump 更清楚地了解应用程序中正在发生的事情”。
BOLT 使用控制流图中基本块的 profile 信息来确定热点代码所在的位置。然而,为了做出最佳的代码布局决策,最好在每一条边上加上权重,而不仅仅是使用基本块的执行次数来决策。LBR 分析可以提供这些信息,但即使没有这些信息,BOLT 也可以复原出一些关于边权重的信息。
然后就是使用该图来优化代码;“给我们带来大部分收益的主要优化是代码重排序。”可以将基本块组合在一起,以减少代码的指令缓存占用空间。这是通过将函数分解成片段来完成的,其中一些片段是热代码,而另一些片段很少或从未执行过(例如错误处理代码)。编译器已经进行了重排序,但是在函数级别上进行的;BOLT 更进一步,对这些函数片段进行重排序。
他说,一旦生成了新的代码,就有一个问题:把它放在哪里。除非非常关心磁盘空间,否则创建包含热点代码的新文本(text)段会更有效率,新文本段通常会小很多(对于一个很大的二进制文件,“几百兆字节”,最终在新段中的热点代码将少于 20MB)。因此,就磁盘空间而言,开销不大,“而且你的应用程序会变得 快得多 ”。
他表示,向内核二进制文件添加另一个文本段可能不可行,因此他转向了一种“更可行”的替代方案。BOLT 将简单地重写二进制文件中现有的函数;编译器已经根据其分析对这些函数进行了排序,因此 BOLT 有效地利用了这一点。BOLT 可以基于 profile 数据对函数进行更好的排序,但为了轻松对基本块进行重排序,可以牺牲这一小小的收益,他说道。这也有助于避免使代码过度专门化那些仅出现在 profile 测量中的工作负载;对于其他未测量的工作负载,一些冷代码将被执行,但由于编译器的选择,它仍然位于附近。
内核带来了其他挑战,因为它的代码在启动时以及运行时都会被修改。他花了很长时间详细介绍了如何处理这类代码。诸如静态调用、SMP 锁(在单处理器系统上被修补掉)、静态密钥和针对不同子架构的替代指令之类的东西都使用反汇编代码中的注释来处理,这是 BOLT 用来完成其工作的元数据的一部分。对于某些代码,BOLT 根本不对它们进行操作,而其他代码则有优化器可以对其使用的机制。所有这些元数据都可以使用 BOLT 工具转储。
Panchenko 说他跳过了一些主题,例如连续分析,它可以应用于运行时的 BOLT 优化二进制文件;可以生成一个新版本的二进制文件,以反映代码和工作负载的变化。他也跳过了其他 BOLT 应用的优化。最后,他展示了运行 BOLT 产生的一些输出,并指出一个四秒钟的运行演示并不会那么有趣。
[ 我要感谢 LWN 的旅行赞助商 Linux 基金会为我前往维也纳参加 Linux Plumbers Conference 提供的旅行援助。]
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

2261

被折叠的 条评论
为什么被折叠?



