本内容是对知名性能评测博主 Anton Putra Actix (Rust) vs Zap (Zig) vs Zig: Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准
介绍
在这期内容中,我想分享关于 Rust 和 Zig 之前的基准测试的最新结果。我收到了很多关于如何优化 Zig 应用程序的建议,还有几个 PR(Pull Request),这些优化带来了巨大的改进。 feat: Fix improper build command Lesson 207 - add pure Zig implementation
我会快速介绍这些优化技术,然后使用优化后的代码重新运行基准测试。此次测试的设置和参数与之前相同,以确保负载生成的一致性。
所以,在本视频中,我们将比较 Rust 和 Zig 这两种编程语言。具体来说,我会使用 Actix 作为 Rust 的 HTTP 框架,而 Zap 则作为 Zig 的 HTTP 框架。此外,我还收到了一位 Zig 专家的 PR,其中包含了一个纯 Zig 编写的 HTTP 服务器实现。
和往常一样,我会施加足够的负载,以找出每个应用程序的极限性能。
Actix(Rust)概览
我选择 Actix 作为测试框架,是基于我之前对最流行的 Rust HTTP 框架(Actix、Axum 和 Rocket)进行的基准测试。在那次测试中,Actix 在这三者中表现出了 最低的延迟 和 最低的 CPU 使用率。
Zap(Zig)概览
接下来是 Zap,一个用 Zig 编写的 HTTP 框架。它本质上是对一个 C 库的封装,并且根据官方提供的基准测试结果,它的性能应该优于 Rust。
在我上次运行基准测试时,我并没有使用所有优化编译选项来编译应用程序。而就在我发布基准测试的几分钟后,我就收到了一个 PR,其中包含了正确的优化方法。
Zig 具有四种构建模式:
- Debug 模式(默认模式):当你直接运行
zig build
命令时,它会使用此模式。这正是我在上次基准测试中使用的模式,导致 Zap 应用程序运行 非常慢。 - ReleaseFast:优先考虑性能,并禁用所有安全检查。
- ReleaseSafe:优先考虑安全性,并保留所有安全检查。
- ReleaseSmall:用于生成 较小 的可执行文件。
你可以在 Zig 的官方文档中关于构建模式和其他设置的信息。
纯 Zig 实现概览
除了 Zap,我还收到了一位 Zig 专家的 PR,其中包含了一个 纯 Zig 编写 的 Web 服务器实现,并且使用了相同的 API 端点。根据本地测试,该服务器的吞吐量比 Rust 还要高,能够处理 接近 200,000 次请求/秒。
该测试是在一台 M2 ARM 架构的 Mac 上进行的。
唯一的不同之处在于,我在编译时选择了 ReleaseFast 模式,以 优先考虑性能 而非文件大小。如果你对 Zig 感兴趣,我建议你阅读该 PR 的代码。
测试方法
为了运行此次测试,我使用了 生产级的 AWS EKS(托管 Kubernetes)集群。我创建了两个 节点组(Node Groups):
- 一个由 xlarge 实例组成,用于运行应用程序。
- 另一个由 4xlarge 实例组成,用于运行生成负载的客户端。
所有源代码都托管在我的 GitHub 公开仓库。
接下来,我们将所有三个应用程序部署到 AWS Kubernetes,并让它们在 空闲状态 下运行 10 到 15 分钟。
CPU 和内存使用情况
让我们先看看 CPU 使用率:
- Rust(Actix) 具有最高的 CPU 使用率。
- 纯 Zig 实现 的 CPU 使用率 低得离谱,甚至 Prometheus 和 cAdvisor 都无法检测到它正在运行,显示为 0%。
接下来是 内存使用情况:
- 如预期的那样,Zap 的 内存使用量最高。
- 具体来看:
- Actix 使用 760 KB。
- Zap 使用 22 MB。
- 纯 Zig 实现使用约 1,000 KB(1 MB)。
虽然这些数据本身并不能说明太多问题,但它们仍然是评估应用程序性能的一个有用基准。
负载测试
现在,我开始部署 测试任务 到 Kubernetes,以生成负载。
- 每个应用程序都有 20 个 Pod 运行。
- 每 30 秒,虚拟客户端的数量会增加 1 个。
- 负载从 20 个客户端 开始,最终大约会达到 4,000 个并发客户端。
在测试的前几分钟,我们可以 忽略延迟波动,直到它趋于稳定。
测试结果
初始阶段
- Rust(Actix) 具有 最低的延迟。
- Zap 和纯 Zig 实现 的延迟相近。
- 纯 Zig 服务器的 CPU 使用率在测试初期要高得多。
- 每个应用实例运行在 独立的 Kubernetes 节点 上,配置为 2 个 CPU 核心 和 256 MB 内存。
50,000 QPS(请求/秒)时的表现
-
延迟排名:
- 纯 Zig 服务器
- Zap
- Rust(Actix)
-
CPU 使用率 也呈现出类似的模式。
-
偶尔会有客户端超时,但 99.999% 的可用性 仍然确保绝大多数请求不会失败。
75,000 QPS 时的表现
- 纯 Zig 服务器的性能开始下降,延迟上升。
- 这个吞吐量几乎是 纯 Zig 服务器的极限。
81,000 QPS 时的表现
- 纯 Zig 服务器 开始落后,因此我将其从图表中移除,稍后再进行回顾。
100,000 QPS 时的表现
- Zap 和 Actix 的延迟和可用性均出现波动。
- CPU 使用率达到 60% 以上,这时应该 水平扩展(扩容),而不是继续增加负载。
125,000 QPS 时的表现
- Zap 达到最大极限,无法处理更多请求。
最终结果
- Rust(Actix) 最高处理 160,000 QPS。
- Zap 最高处理 127,000 QPS,CPU 使用率接近 90%。
完整测试数据
- 请求处理速率(QPS)趋势图
- 客户端延迟趋势图
- 各应用程序 CPU 使用率
- 内存使用情况
- 可用性(成功请求率)趋势图
- Kubernetes CPU 限流情况
(图略)