1. 引言

在部署 AI 加速服务时,总是会遇到一个绕不开的话题,那就是你的服务,到底部署的好不好?别人怎么知道你的代码,你的逻辑是否是最佳,或者是实践上的最佳?所以需要一些客观的指标来告诉别人,目前承载的压力以及潜力。

那么很自然地,我们会想到这么一个指标:吞吐 (throughput). 那么吞吐的数学含义怎么定义?下面引出规则定义 1.

$$text{Throughput} = frac{text{Executed numbers}}{text{Unit time}} tag{1}$$

2. 吞吐—衡量指标的桥梁

那么吞吐就很直观,比如,我部署了单机的sglang 的服务节点,这个语境下,我们谈论吞吐就是指,一秒里,这个节点能出多少 token?但是这里还有一个问题,人类对世界的感知其实是对数的。具体这里不做过多叙述,具体可以b 站查看毕导的视频。所以,比如目前一个配置是 26896 的吞吐,另一个是 28768 的吞吐,我们可以很直观地得出后者吞吐更高的结论,但是好多少,估计不少人就不能一口说出来了。这就是人类对较大数据缺少敏感的bug 了。为此,我们其实还会引入其他指标,辅助我们进行比较。这里引出规则定义 2.

$$text{QPS} times text{output_len} = text{throughput} = text{bs} times text{OTPS} tag{2}$$

这里做一下参数解释:QPS,指每秒发送的请求数,表达服务接受的的并发数,这也经常作为一个技术指标,比如淘宝的秒杀,支付宝的支付接口等等,用于衡量服务能扛住多少用户的请求。output_len 是指一个请求的输出长度。为了方便衡量性能,我们在做测试或者思想实验时,假设 output_len 是一个恒定的值。好了,这就是等式左边的含义,这两个参数是站在用户的角度进行考虑的,在用户角度,压根不管你服务的后端是怎么优化的,只在乎你的服务能接住我多大的流量。同样地,也可以认为左边等式的含义是站在结果的角度考虑:output_len 是已知的,在乎的是服务端完成等长输出时,能同时处理多少个请求。

而在等式右边,其实已经站在了开发者的角度来看问题。bs 是指引擎层面的指标,比如 sglang 就经常会出现 batch_size 的相关指标。而 OTPS 是指每秒输出多少 tokens 的简写(Output Tokens Per Second, OTPS), 这个是指一个请求,每秒能回答(输出)多少个 token,这个是决定了引擎的可用性的。为什么呢?因为这个是衡量用户一条请求发送后,显示回答的速度,如果一秒才 6~7 个 token,换算成中文字符,也就是 3~4 个汉字,显然是太慢, 已经在耐心的边缘了。所以,在实际部署服务时,这个指标往往都是一个硬性限制(大于 20),这个后面会细讲。整体来看,等式右边也可以说是站在过程的角度考虑:细化到每一个 req 的输出 token 的速度限制,以及引擎推理过程中的 bs 压力。

3. 推理阶段指标的互相转换

下面我们一起来探讨一下,多个计算指标之间的转换逻辑。(先讨论 Decode 节点)

指标 计算公式 相关指标备注
吞吐(ThroughPut) 1) QPS output_len; 2) BS MTP_rate DP_Num OTPS 所有指标均与之有关
并发大小 (QPS) 从吞吐反算 or 固定 QPS 压测
并行大小 (Batch Size) 引擎启动时固定 or 固定 bs 压测 注意是每个 DP 的 bs
每条请求每秒输出 tokens (OTPS) 1s / Forward_TIme * MTP_Rate 和 TPOT 互为倒数
每输出一个 token花费时间 (TPOT) 1s / OTPS 和 OTPS 互为倒数
前向时间 (Forward Time) MTP_Rate / OTPS 绝对值,由引擎的 metrics 中获取

[!important]

Q:引擎的吞吐是否可以一直推高?极限在哪里?

A:当然不会一直推高;假设我们站在用户侧视角,我们可以通过固定数据集,不断提高QPS 进行压测。但是单位时间内,部署在 GPU 上的代码,其实处理能力是固定的,GPU 的极限性能也存在;所以理论上,引擎的极限性能就应该等于硬件的极限性能。但是由于服务的复杂性,性能不断损失,可能实际能发挥硬件的 30% 就算很优秀了。因此,测试引擎性能这件事才变得重要。

[!important]

Q:那引擎其实是一个黑盒,如何快速测试到性能极限?