使用Prodfiler优化eBPF优化器:零代码改动实现性能翻倍

本文详细介绍了如何利用Prodfiler性能分析工具对K2(eBPF优化编译器)进行深度优化。通过替换内存分配器、优化数据结构实现和启用PGO/LTO编译优化,最终实现1.4-1.9倍的性能提升,显著提升编译器搜索效率。

使用Prodfiler优化eBPF优化器(重发版)

如何在不修改任何代码的情况下将应用程序性能提升近2倍?请继续阅读!

这是我在2021年10月4日最初发表在prodfiler.com上的博客重发版。Prodfiler已被Elastic收购,现为Elastic Universal Profiler。

在本文中,我将逐步介绍如何使用Prodfiler发掘K2(eBPF优化编译器)中的优化机会。K2完全受CPU限制,采用引导式搜索技术,依赖高速创建和检查候选方案的能力。通过Prodfiler,我们可以轻松发现K2中消耗最多CPU周期的组件,从而进行相应优化。最终结果是K2版本速度提升1.4-1.9倍,意味着在相同资源下可以探索更大的搜索空间。

预期改进来自:

  • 用性能更好的mimalloc替换系统分配器
  • 辅助编译器自动向量化热点循环
  • 对K2及其重要依赖Z3应用PGO和LTO

引言

随着eBPF更多用例的发现(许多处于关键路径),编译器生成的eBPF代码性能日益重要。虽然clang通常能生成高效程序,但有时会产生存在冗余、指令序列选择异常、操作数宽度不必要狭窄等问题的代码,导致手动优化程序明显更快。手动优化eBPF指令具有挑战性、耗时且容易出错,因此与传统目标平台编译一样,存在投入编译时间以换取运行时性能提升的工具市场。

罗格斯大学研究人员今年8月发布了K2,这是eBPF代码的优化编译器。K2输入现有eBPF程序,搜索语义等效但更小更快的程序。论文证明该方法可减少程序大小6-26%,降低平均包处理延迟1-55%,相对最佳clang编译程序提升吞吐量达5%。

K2架构概述

K2核心采用MCMC与Metropolis-Hastings接受准则。算法核心是:

  1. 从当前状态生成新候选
  2. 为候选分配成本
  3. 根据候选成本与当前状态成本有条件更新当前状态

算法重头戏在步骤2。为计算候选成本,K2使用自定义用户空间eBPF解释器在输入集上解释候选。如果候选产生与原程序相同输出,K2将候选转换为符号表示并使用Z3定理证明器进行等价检查。因此MCMC搜索内循环计算密集,每个候选需要大量计算工作。

设置基准测试

K2可在GitHub找到,作者还上传了包含测试和基准的会议工件。我克隆此仓库添加了辅助脚本。主要K2仓库缺乏安装运行信息,但幸运的是有安装脚本可遵循。脚本在11个不同eBPF程序上调用K2寻找更高效实现,我们将以其为基础进行基准测试。

使用Prodfiler进行基准测试和优化

Prodfiler入门简单。按照文档创建新项目并部署后,运行上述基准测试收集基线数据。我创建的脚本默认每个基准运行15次,还提供"快速"模式快速验证假设。

第一幕:内存分配优化

Top N Functions视图显示malloc和free函数位列第5和第6。扩展列表发现内存管理函数占前20位中5席,总计消耗应用10% CPU时间!我们选择用mimalloc替换系统分配器。

使用mimalloc后,free函数完全退出前10,内存管理总开销降至约5%。基准测试显示平均加速1.08倍,最小1.05倍,最大1.12倍。

第二幕:优化std::vector

Top N Functions前两位占15% CPU预算,涉及std::vector操作。分析调用图发现prog_state::init_safety_chk和inout_t::operator=是主要调用源。

这些向量大小固定(11和512),但编译器未能自动向量化复制循环。将bool替换为uint8_t性能改进微弱,因uint8_t存在别名问题。改用C++20的char8_t类型后,编译器生成直线SIMD代码。

此更改带来平均1.31倍加速(相对mimalloc版本),最大1.57倍,最小1.12倍。相对原始版本平均加速1.43倍,操作符开销从12.3%和3.93%降至0.82%和0.62%。

第三幕:优化Z3

火焰图显示Z3_solver_check()及其子函数占K2工作量的46%。我们选择通过PGO和LTO优化Z3编译方式。

对Z3应用PGO和LTO后,平均获得1.1倍性能提升。最终对K2本身应用PGO,再获平均1.03倍提升。总体平均改进1.62倍,最大1.91倍,最小1.42倍。

结论

本文展示了如何使用Prodfiler作为迭代过程的一部分显著提升应用性能。我将Prodfiler视为"疑问生成器",其各种视图能引发"这很奇怪"的思考,从而发现消耗CPU的意外特性。结合持久性,现代软件栈中存在巨大性能改进空间。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计