跳到主要内容
英文版本为准

本页译自英文,由模型翻译。若与英文原文存在差异,以英文原文为准。

方法论

ruststream-zeromq 如何测量自己:三个循环、用同一进程中的对端代替 Broker、受传输限制的判定计算,以及代码计数。流程就是框架自己的流程(RustStream 方法论),本页说明它在 ZeroMQ 上是什么样子。

三个循环​

一个进程把每个场景跑三遍,作为三个循环,它们只有一处不同:承载消息的是什么。

  • 裸客户端。 zeromq 套接字,直接驱动:接收、解码。
  • Broker crate。 本 crate,上面什么也没有:Broker 建立连接,打开一个订阅,一个循环从它的流中取出消息、解码并 ack。
  • RustStream。 用户写的服务:一个 #[subscriber] 处理器及其外围的应用。

其余一切在三个循环中都相同:套接字模式、套接字选项、线上的三帧、解码成同一个结构体、载荷字节、tokio 运行时,以及二进制程序。

消息体是 512 字节的 JSON。它解码成一个包含两个整数字段的结构体,因此解码不做内存分配,数字反映的是本 crate,而不是 JSON 解析器。

同一进程里的对端​

框架的其他每个 Broker crate 都拿自己和一台服务器对照:启动一个容器,客户端连接它,这一行就是该 crate 在这个客户端之上增加的开销。ZeroMQ 的对端彼此直接通信。每个循环的另一端都是同一进程里的一个 PUSH 套接字,使用默认的套接字选项,运行时除了二进制本身不启动任何东西。

路径上没有服务器的传输,每条消息的开销很低。于是本 crate 自己的工作在总开销中所占的份额比在其他任何地方都大,同样的开销在这里读出的百分比,比对照 Broker 时更大。

运行​

  • 每次运行使用新地址,环回地址上的新端口或新的 ipc:// 路径,因此一次运行永远遇不到之前的运行留下的连接。
  • 在三个循环中,计时窗口都从第一条消息开始,到最后一条消息解码完成时结束。 绑定和握手都在窗口之外。
  • 消息条数来自一次探测,它测量裸循环的速率,因此在任何机器上,测量的运行都至少持续五秒。
  • 各循环交替进行:裸、crate、RustStream,共三轮。每个循环报告它最佳、中位和最差的一轮。

当一条消息的大部分时间都在等待传输,因而上层的工作发生在已经付出的等待之内时,该行标为 受传输限制。这是算出来的:同一传输上一次交换的往返时间,乘以一条消息花费的往返次数,再与测得的每条消息的时间相比。往返时间在所有循环之外用一对 REQ/REP 测量,并与机器信息一起公布。PUSH/PULL 消费者不回发任何东西,因此一条消息花费零次往返,没有任何一行受传输限制。

代码计数​

开销图的每个场景都是用户写的服务,基于生产 Broker,绑定到环回地址上的一个 TCP 端口,构建时不带 testing 特性。对端是一个裸 zeromq 套接字,运行在自己的线程上,写出本 crate 的三帧。

对端在计数开始之前、服务还没有读取时写出全部消息,消息在内核的缓冲区里等待。PUSH/PULL 会为尚未读取的消费者保留它们,因此消费场景和批场景使用它。应答场景是 DEALER/ROUTER,回答发回提出请求的对端,它的计数一直持续到对端读完所有回答。

指令数来自 callgrind,内存分配来自 DHAT。一个场景依次在一条消息、1000 条、2000 条上运行。后两次之差是一条消息的稳态开销,单条消息就是冷启动。

同一个二进制程序运行三次,消费场景的指令数完全相同,另外两个场景相差在千分之三以内:对端如何读取回答、批何时结束,取决于时序。内存分配次数相差在一个块以内。与保存的基线对照时,某个场景的指令数多出百分之二就算失败。内存分配在每次运行中都受每个场景声明的下限约束。

自己运行​

just bench

该命令运行两个对照场景,并重写 docs/benchmarks/results.json。它不启动任何测试环境,因为对端是同一进程里的套接字。它需要几分钟,并要求独占机器。它会清空 RUSTFLAGS,因此二进制程序不绑定于构建它的 CPU。两个命令都按发布构建的方式构建基准测试,在各 crate 之间使用完整的链接时优化(fat LTO),因此数字描述的是服务实际发布的代码。

just bench-code

代码计数只需几秒,需要 valgrind。该命令自己安装基准测试运行器,版本为发布版 Cargo.lock 固定的版本。在 main 上运行 just bench-code --save-baseline=main 记录基线,在分支上运行 just bench-code --baseline=main 与之对照。just bench-code 2000 让每个场景在两千条消息上计数,而不是一千条。