Rustls:持续基准测试案例研究
Everett Pompeii
什么是 Rustls?
Rustls 是一个用 Rust 编写的现代化传输层安全协议(TLS)库, 旨在替换诸如 OpenSSL 这样的非内存安全替代品。 TLS 协议通常用于提供安全通讯, 主要在网络服务器和客户端之间。 TLS 之前叫做安全套接字层 (SSL)。 它确保了在两方之间传输的数据被加密, 并且安全无法被窃听或篡改。 因此,像 Rustls 这样的 TLS 库既要快速也要保证安全。
🐰 在
https
中的s
表示你正在使用 TLS 来查看此页面!
对 Rustls 的基准测试
Rustls 的第一次提交是由项目的创建者 Joseph Birr-Pixton 在 2016 年 5 月 2 日进行的,而第一次成功的 TLS 连接直到同月的 27 日才发生。 到了那一年的 9 月,他已经开始对 Rustls 进行基准测试了。在他的带领下,项目做出了显著的性能改进, 他在 2019 年 7 月进行了 Rustls 与 OpenSSL 的头对头的基准测试对比。
那次基准测试对比的结果如下:
- Rustls 发送数据要快 15%。
- Rustls 接收数据要快 5%。
- Rustls 建立客户端连接要快 20-40%。
- Rustls 建立服务器连接要快 10%。
- Rustls 恢复客户端连接要快 30-70%。
- Rustls 恢复服务器连接要快 10-20%。
- Rustls 使用的内存不到 OpenSSL 的一半。
在 2023 年,互联网安全研究组 资助了对 Rustls 项目的性能基准测试,并且产生了 更新的 Rustls 与 OpenSSL 的头对头基准测试对比。虽然这些更新的结果对 Rustls 项目在优势区域和改进区域的了解都很有帮助,但现在最大的性能关注点是保证_新的_代码不会引入性能回归。
Rustls的持续性基准测试
为了在发布之前发现性能退化,Rustls项目决定投资于持续性基准测试。
持续性基准测试是一种软件开发实践,团队成员经常对他们的工作进行基准测试, 通常每个人每天至少完成一次基准测试 — 这将导致每天进行多次基准测试。 每次基准测试都由一个自动化构建来验证,以尽快发现性能退化。 许多团队发现这种方法显著减少了性能退化 并且使团队能更快地开发高性能的软件。
Rustls项目的持续性基准测试解决方案主要包括两个组件:
- CI Bench:一个专门为CI中运行基准测试而设计的自定义基准测试工具
- Bench Runner:一个定制的,裸机持续基准测试服务器和配套的GitHub应用
Rustls CI 基准测试
CI 基准测试是持续基准测试的最佳工具。 它以两种不同的模式运行完全相同的基准测试:指令数模式和墙钟时间模式。 这是通过一个独创的自定义异步运行时实现的。 对于指令数模式,I/O 实际上仍然是阻塞的。 在内部,任务只在一次轮询中完成。 然后对于墙钟时间模式,I/O 真的是非阻塞的。 这允许模拟共享的内存缓冲区。 服务器和客户端交替进行轮询。 这使得 CI 基准测试可以消除异步运行时在其基准测试中的噪声和非确定性。
Rustls 选择使用 cachegrind 来跟踪 CPU 指令。 此决定是模仿 Rust 编译器的持续基准测试解决方案。 指令计数提供了一种非常稳定的方式来比较同一软件的两个版本。 这使其成为持续基准测试的理想选择。 然而,无法推断出指令计数增加的实际运行时成本。 指令增加 10% 不一定意味着运行性能增加 10%。 但是指令数量的大幅增加可能意味着运行性能有所增加。 因此,CI 基准测试也测量墙钟时间。
墙钟时间是 Rustls 项目真正关心的事情。 测量指令计数只是一个有用的代理。 基于指令计数的基准测试无法对使用相同数量指令但导致截然不同的墙钟时间性能的更改进行二者区分。 例如,新的算法可能恰好拥有完全相同的指令数,但运行速度慢两倍。
Rustls 测试套件运行器
Rustls Bench Runner 是一个定制的持续基准测试服务器。
它设计为在裸金属主机上运行,
它通过 webhooks 从伴随的 GitHub App 接收事件。
每次推送到 main
分支时,
Bench Runner 都会运行指令计数和预计运行时间的基准测试。
结果被存储在本地并
通过 Bencher API 发送到 在 Bencher 上的 Rustls 项目。
每当一个拉取请求被批准
或者 Rustls 维护者留下包含 @rustls-benchmarking bench
的评论时,
基准测试套件就会运行。
Bench Runner 会从 GitHub 接收一个 webhook,
拉取拉取请求的代码,
运行指令计数基准测试,
运行预计运行时间的基准测试,
将拉取请求的结果与目标 main
分支的结果进行比较,
然后将结果作为一个评论发布在拉取请求上。
Bench Runner 使用一个 Delta 四分位距模型 作为它的统计阈值
用来确定是否发生了性能回归。
超过此阈值的结果将在拉取请求评论中被高亮显示。
裸金属服务器
为了获得他们在墙时间基准测试上的1%的分辨率,Rustls项目投资了一台特殊配置的裸金属持续基准测试服务器。与大多数现代CI运行者不同,这台服务器并非短暂存在。也就是说,每次运行都使用相同的底层服务器硬件和操作系统。没有虚拟化。
裸金属服务器已经特别配置,以便创建尽可能一致的结果。频率缩放(Intel 的 TurboBoost)和同时多线程(Intel的超线程)在 BIOS 中都已禁用。CPU缩放设置为性能
。通过在 sysctl.conf
中设置kernel.randomize_va_space=0
和 kernel.nmi_watchdog=0
, 分别禁用了地址空间布局随机化(ASLR)和非屏蔽中断(NMI)看门狗。裸金属服务器由OVHcloud托管。
奖杯橱窗
- PR #1640: Rustls持续基准化集成中最早的应用之一,用于评估从
write_vectored
到write
的过渡。这个改变在某些情况下将Rustls发送方向的传输基准提高了近20%。 - PR #1730: 发现了在随机化TLS ClientHello扩展的顺序时的性能退化。这导致开发的进一步迭代和更有效方法的使用。
- PR #1834: 帮助快速验证关键安全修复没有引入性能退化。这使得团队能够专注于快速发布多个安全修补版本。
总结
在项目创始人设定的基础上,Adolfo Ochagavía为 Rustls 项目构建了一套令人印象深刻的连续基准测试解决方案。这包括一个自定义的基准测试工具链,它可以将指令数量和挂墙时间基准测试运行在同一个测试中,一个自定义的基准测试运行器,一个自定义的 GitHub 应用,以及一个自定义的专用裸金属服务器。这是目前最令人印象深刻的特定项目连续基准测试解决方案之一。如果你的项目有时间和资源来构建和维护一个定制的连续基准测试解决方案,那么 Rustls 项目设定了一个高昂的目标。
特别感谢 Adolfo Ochagavía 对这个案例研究的审查。他关于 Rustls 的连续基准测试和 Rustls 性能的博客文章是这个内容的基础。
Bencher: 持续性能基准测试
Bencher是一套持续型的性能基准测试工具。 你是否曾经因为性能回归影响到了你的用户? Bencher可以防止这种情况的发生。 Bencher让你有能力在性能回归进入生产环境 之前 就进行检测和预防。
- 运行: 使用你喜爱的基准测试工具在本地或CI中执行你的基准测试。
bencher
CLI简单地包装了你现有的基准测验设备并存储其结果。 - 追踪: 追踪你的基准测试结果的趋势。根据源分支、测试床和度量,使用Bencher web控制台来监视、查询和绘制结果图表。
- 捕获: 在CI中捕获性能回归。Bencher使用最先进的、可定制的分析技术在它们进入生产环境之前就检测到性能回归。
基于防止功能回归的原因,在CI中运行单元测试,我们也应该使用Bencher在CI中运行基准测试以防止性能回归。性能问题就是错误!
开始在CI中捕捉性能回归 — 免费试用Bencher Cloud。