本页译自英文,由模型翻译。若与英文原文存在差异,以英文原文为准。
基准测试
ruststream-fred 在每条消息上的开销:与裸 fred 客户端对照的吞吐量,以及它自身代码的指令数和内存分配次数。
与裸客户端对照
第一张图在真实的 Redis 服务器上把每个场景用三种方式运行。裸客户端 是在 fred 上手写的循环。Broker crate 是在本 crate 上写的同一个循环:Broker、订阅、投递流和 ack,上面没有运行时。RustStream 是用户写的服务:应用里的一个 #[subscriber] 处理器。第一项差值是本 crate 比它所封装的客户端多付出的开销,第二项是运行时在其上增加的开销。
每个循环画成一条横条,从最佳一轮延伸到最慢的一轮,圆点标出最佳一轮,缺口标出中位一轮。坐标轴的零点是裸客户端的最佳一轮,圆点在零点右侧表示更慢。空心圆点表示该循环与裸客户端的差异小于各轮的波动范围。图表下方的表格列出最佳一轮,括号里是中位一轮。
测量的形式有三种,每种传输方式一种:通过消费者组读取并确认每个条目的流、可靠列表,以及一个 Pub/Sub 频道。每种形式都分别在单机服务器、集群和 Sentinel 之后的主节点上运行。在集群上,Pub/Sub 一行使用分片形式,即 SSUBSCRIBE 和 SPUBLISH,集群上用的正是这种形式。
代码的开销
第二张图统计一条消息在服务线程上的开销:稳态下 callgrind 测得的指令数和 DHAT 测得的内存分配次数。每个场景都是一个基于 RedisBroker 的服务,连接测试环境的单机服务器。服务运行在单线程运行时上,fred 也在同一线程上驱动它的连接,因此计数包括运行时、本 crate,以及 fred 编写命令和解析应答的工作。服务器是另一个进程,不计入。冷启动 是连接连接池、创建组、打开订阅并取得第一次投递一次性付出的开销。
流场景用 XREADGROUP 读取消费者组,用 XACK 结算,用 XADD 应答。可靠列表用 BLMOVE 认领每个条目,用 LREM 结算它。Pub/Sub 场景读取一个频道或一个模式,不结算任何东西。.pipeline() 场景把一次读取的结算一起发送,它的处理器每次投递排入一条 INCR;.pipeline().atomic() 把每次投递的段包进 MULTI 和 EXEC,窗口一页用图说明了这一点。
结果
- 裸客户端
- Broker crate
- RustStream
- 各轮,从最佳到最慢
- 最佳一轮
- 中位一轮
- 轮次的波动范围
- 无法区分
ruststream-fred 0.7.0 on ruststream 0.7.0-rc.11, AMD Ryzen 9 7900X 12-Core Processor, Linux 7.2.7-x64v3-xanmod1-1, redis:7-alpine in Docker on the host network, persistence off: a standalone server, a six-node cluster and a master behind Sentinel, 测量于 2026-09-27. 这些数字是怎样测得的
- 无法区分
- 无法区分 (受 Broker 限制)
- 无法区分
- 无法区分 (受 Broker 限制)
- 无法区分
- 无法区分 (受 Broker 限制)
- +2%
- +3.3% (受 Broker 限制)
- 无法区分
- 无法区分 (受 Broker 限制)
- 无法区分
- 无法区分 (受 Broker 限制)
- 无法区分
- 无法区分
- 无法区分
- 无法区分
- 无法区分
- 无法区分
以表格显示数字
| 场景 | 裸客户端 | Broker crate | RustStream | 额外开销 |
|---|---|---|---|---|
| Redis Streams consumer group, standalone, 512 B JSON, ack each | 51,326 msg/s (51,293) | 51,036 msg/s (50,247) (比客户端 无法区分) | 50,739 msg/s (49,943) | 无法区分 (受 Broker 限制) |
| Redis Streams consumer group, cluster, 512 B JSON, ack each | 39,596 msg/s (34,982) | 38,674 msg/s (34,977) (比客户端 无法区分) | 38,363 msg/s (34,554) | 无法区分 (受 Broker 限制) |
| Redis Streams consumer group, sentinel, 512 B JSON, ack each | 40,743 msg/s (40,444) | 40,202 msg/s (40,034) (比客户端 无法区分) | 40,213 msg/s (38,946) | 无法区分 (受 Broker 限制) |
| Redis list work queue (reliable), standalone, 512 B JSON, ack each | 29,548 msg/s (29,342) | 28,970 msg/s (28,639) (比客户端 +2%) | 28,575 msg/s (28,514) | +3.3% (受 Broker 限制) |
| Redis list work queue (reliable), cluster, 512 B JSON, ack each | 22,656 msg/s (22,216) | 22,449 msg/s (22,290) (比客户端 无法区分) | 22,128 msg/s (22,002) | 无法区分 (受 Broker 限制) |
| Redis list work queue (reliable), sentinel, 512 B JSON, ack each | 23,372 msg/s (21,630) | 22,720 msg/s (22,374) (比客户端 无法区分) | 22,562 msg/s (21,649) | 无法区分 (受 Broker 限制) |
| Redis Pub/Sub, standalone, 512 B JSON, no acknowledgement | 402,530 msg/s (397,136) | 400,183 msg/s (400,038) (比客户端 无法区分) | 397,976 msg/s (394,780) | 无法区分 |
| Redis Pub/Sub (sharded), cluster, 512 B JSON, no acknowledgement | 297,850 msg/s (295,380) | 295,819 msg/s (292,015) (比客户端 无法区分) | 290,276 msg/s (289,083) | 无法区分 |
| Redis Pub/Sub, sentinel, 512 B JSON, no acknowledgement | 358,378 msg/s (353,940) | 355,630 msg/s (352,911) (比客户端 无法区分) | 395,136 msg/s (351,006) | 无法区分 |
AMD Ryzen 9 7900X 12-Core Processor, x86_64, 427-5912 MHz, 12 physical, 24 logical, 62.4 GiB, unknown, Linux 7.2.7-x64v3-xanmod1-1, 1.98.1, 3.25.1, bench, inheriting release (opt-level = 3, lto = false, codegen-units = 16), ruststream-fred default (none), ruststream macros,json (the service loop only). ruststream-fred 0.7.0, ruststream 0.7.0-rc.11, 测量于 2026-09-27.
以表格显示数字
| 场景 | 指令数 | 内存分配 | 冷启动 |
|---|---|---|---|
| stream consumer group, JSON decode into a small struct, XACK each | 32,860.7 | 29.7 | 777,211 / 467 |
| stream reply, appended with XADD through RedisPublish | 53,459.4 | 44.7 | 814,951 / 483 |
| stream consumer group in batches of 64, XACK each | 33,166.8 | 29.8 | 798,525 / 470 |
| reliable list, JSON decode into a small struct, LREM each | 39,668 | 26 | 727,658 / 415 |
| Pub/Sub channel, JSON decode into a small struct | 8,768.7 | 6 | 916,144 / 402 |
| Pub/Sub pattern, JSON decode into a small struct | 9,135.9 | 6 | 915,955 / 397 |
| stream with .pipeline(), one INCR queued per delivery | 38,225.6 | 39.2 | 820,308 / 515 |
| stream with .pipeline().atomic(), one INCR queued per delivery | 78,086.7 | 71.8 | 839,410 / 523 |
| reliable list with .pipeline(), one INCR queued per delivery | 57,556.1 | 48.9 | 2,630,559 / 1,941 |
| reliable list with .pipeline().atomic(), one INCR queued per delivery | 82,776.9 | 68.6 | 2,648,107 / 1,949 |
| Pub/Sub channel with .pipeline(), one INCR queued per delivery | 31,151.8 | 27.1 | 946,874 / 433 |
| Pub/Sub channel with .pipeline().atomic(), one INCR queued per delivery | 56,240.5 | 47.3 | 963,733 / 447 |
| Pub/Sub pattern with .pipeline(), one INCR queued per delivery | 31,525.9 | 27.1 | 945,783 / 428 |
| Pub/Sub pattern with .pipeline().atomic(), one INCR queued per delivery | 56,420.4 | 47.3 | 962,695 / 442 |
机器
| 处理器 | AMD Ryzen 9 7900X 12-Core Processor |
|---|---|
| 架构 | x86_64 |
| 频率 | 427-5912 MHz |
| 核心数 | 12 physical, 24 logical |
| 内存 | 62.4 GiB |
| 内存速度 | unknown |
| 操作系统 | Linux 7.2.7-x64v3-xanmod1-1 |
| Broker | redis:7-alpine in Docker on the host network, persistence off: a standalone server, a six-node cluster and a master behind Sentinel |
| 往返时间 | standalone 22 µs, cluster 25 µs, sentinel 24 µs (one EXISTS and its answer) |
| 编译器 | 1.98.1 |
| 构建配置 | bench, inheriting release (opt-level = 3, lto = false, codegen-units = 16) |
| 特性 | ruststream-fred default (none), ruststream macros,json (the service loop only) |
| RUSTFLAGS | none (the recipe clears RUSTFLAGS, so the numbers are not tied to this CPU) |
| Valgrind | 3.25.1 |
数字来自本版本的 docs/benchmarks/results.json,由基准测试命令写入,测量时使用的构建配置就是上面记录的那一个。方法论说明它们是怎样测得的,数字说明可以从中得出什么结论。