基准测试¶
这个 crate 站在 Pub/Sub 客户端和服务之间:它打开流式拉取,把每次投递变成带头的消息,确认它,并
在回程上发布。这一页说明这要花多少,参照物是同样的活用 google-cloud-pubsub 手写一遍。
同一个进程用三个循环跑同一个场景,它们各自只差一件事:由什么来承载消息。
- 裸客户端 直接驱动
google-cloud-pubsub。 - 适配层 只驱动这个 crate,不含其上的任何东西:它的订阅、它交出的投递、它的发布者,由一个 外面没有服务的循环来读取和确认。
- 服务 就是你自己会写的那一整套:订阅者处理器、应用和运行时。
适配层这一列对着裸客户端那一列,是这个 crate 自身的开销,这个数字由本仓库负责。服务这一列对着 适配层那一列,是运行时叠在它上面的开销,而且是在 Pub/Sub 上的那一份。
其余一切都保持相同:端点和凭据、拓扑、流式拉取的设置、确认(ack)的位置、解码成同一个类型、 载荷字节、tokio 运行时和构建。这套流程属于框架本身,写在 RustStream 基准测试页 上;这一页公布它在这台机器上得出的结果。
下面每一个数字都取自 docker-compose.test.yml 起的本地模拟器,没有一个取自云上的服务。
数字¶
三个交错轮次中的最佳值,括号里是中位的一轮。越大越好。
表格由浏览器从上一次运行写下的文档读出,所以这一页上没有任何会过期的副本。
第二个场景带着排序键发布,这正是开启了排序的订阅必须按键保序的那种投递形态。它同时给适配层 多加了第一个场景没有的一份活:键被搬进投递的头里,不指名 Broker 的服务就从那里把它当作分区键读 出来。客户端上的循环读消息上的字段,什么也不复制。
标为「无法区分」的一行,是两半之间的差值小于各自多次运行之间离散范围的那一行。凡是传输本身比 分发贵得多的地方,这就是诚实的结果。低于运行噪声的数字读起来像是从未测到过的精度,所以不公布。
同一次运行的机器可读形式在
benchmarks/results.json,
框架的站点用它拼出跨 Broker 的汇总表。
crate 自身的代码¶
第二张表不计时,而是数一条消息在服务线程上花掉多少:指令数由 callgrind 统计,内存分配次数由 DHAT
统计。每个场景都是用户会写的那种服务:它建在 PubSubBroker 上,对着同一套环境里的模拟器启动。服务
只在一个线程上运行,这个线程上的一切都计入:框架的分发、本 crate 的代码,以及 google-cloud-pubsub
客户端,它的流式拉取、确认、确认截止时间的延长和发布请求都在这里运行。模拟器是另一个进程,不在数字里。
消息在开始消费之前从另一个线程发布,这部分工作也不在数字里。
指令数和分配次数都是稳态下每条消息的值:1000 次投递的运行和 2000 次投递的运行之间的斜率。最后一列 是启动服务并处理第一次投递一次性付出的开销。这些数字是绝对值,客户端和框架的工作都算在内;框架单独的 开销由核心库在它的基准测试页上公布。
每一行的大头都是客户端。回复那一行最重:运行时要等每条回复得到确认,才去取下一次投递,所以每条回复 都单独作为一个发布请求发出。冷启动的开销也几乎全在客户端:它要构建两个管理客户端,每一个都会读取并 解析这台机器的根证书,所以这一列取决于机器上的证书库。
客户端按批确认,并按定时器延长确认截止时间,所以每次运行的计数都会有一点浮动:四次运行里,每条消息
的指令数相差不超过百分之 1.2;最长那次运行的分配次数,回复那一行相差不超过 640 次,另外两行不超过
20 次。just bench-code 在分配次数超过场景声明的下限时失败(下限是见到过的最大计数,再加上不小于
这个浮动、也不小于千分之一的余量),加上 --baseline=main 时,指令数多出百分之二以上也算失败;改变
开销的合并请求要附上自己的数字。
机器¶
构建标志和数字一起公布,因为它们会改变这些数字。用 -C target-cpu=native 构建出的二进制给出的
结果,换一台机器就复现不了,所以这条 recipe 在构建前先把这个变量清空。
这些数字不代表什么¶
模拟器不是那个服务。它在回环地址上用明文应答同一套 API,把 TLS 握手、凭据刷新和到某个地域的往返 都留在了外面。一对里的两半对着同一个模拟器说话,所以它们之间的比较仍然成立;速率本身则是一个 本地数字,这里没有任何东西能预言一个订阅从某个地域投递多少。
也正因为如此,「受 Broker 限制」这个标记值得先读。带着它的一行是被模拟器定的速:两半在同一份等待 里跑完了整轮,它们之间的差值是分发开销的下界,而不是对它的测量。这个标记由算术定出:一次在配对 之外的探测量出到模拟器的一个往返,一次投递按它的确认计一个往返,当这个乘积覆盖了一条消息所用 时间的一半以上时,这一行就被标记。往返时间和机器一起公布在下面,算术可以核对。第一行离这条线只差 几个百分点,每次运行都可能落在线的这一侧或那一侧,所以它有没有这个标记,都说明不了消费者是否曾经是 瓶颈。
流控是模拟器不复现的第二样东西。它能发多快就发多快,不管订阅上写着什么上限,所以两半都遇不到云上 的订阅对落后的消费者施加的那次暂停。
这里只有一个消费者、一个订阅、一个很小的消息体,以及一台跑在回环地址上的模拟器。它测的是一次 投递在这个 crate 里的开销,不是 Pub/Sub 能扛多少。这里的一行也不能拿去和另一个 Broker 公布的 一行比较:不同的传输在每条消息上做的事并不一样。
一次运行的测量窗口从第一次投递开始,到最后一次投递被确认为止,两半都是这样。
这些数字是一台机器在某一天的快照。 它们由人手工重测,测量期间机器只跑这一项:这一页要讲的差值,比与其他任务共用的机器上的噪声还小。
自己跑一遍¶
这条 recipe 从 docker-compose.test.yml 起停模拟器,跑完两个场景,然后把测到的结果写回
docs/benchmarks/results.json。它要花几分钟,并且需要整台机器。消息条数不是固定的:一次试探
运行会把它定下来,使得每一次被测量的运行在所在机器上都不短于五秒。
这条 recipe 先起模拟器,在 valgrind 下统计代码表,再把模拟器停掉,并重写同一份文档里的 code
部分。它要花几分钟。除了 Docker,还需要 valgrind 和基准测试运行器:
cargo install --locked gungraun-runner --version =0.19.4。