吞吐量概念与度量:从“快不快”到“多不多”
在诊断 vLLM 性能问题时,我们首先要回答一个看似简单的问题:系统到底跑得多快? 然而在 LLM 推理场景中,“快”并不只有一个维度。用户感受到的“快”是首字延迟低、生成流畅;而运维和成本控制关心的“快”是单位时间内能处理多少请求、产出多少 token。吞吐量(Throughput)正是从“数量”维度刻画系统能力的核心指标。它不回答“单个请求多快”,而是回答“系统整体多能装、多能产”。
3.1 吞吐量的本质定义
吞吐量指系统在单位时间内成功处理的工作量。对于 vLLM 这类文本生成服务,工作量有两种天然计量单位:请求数(requests)和Token 数。因此,吞吐量不是一个单一数值,而是一组从不同视角观察的指标。理解它们的区别,是后续调优的前提。
3.2 核心度量指标详解
在实际工程中,我们通常用以下三个指标构成吞吐量度量的“铁三角”:
| 指标 | 定义 | 典型单位 | 关注点 |
|---|---|---|---|
| Token 吞吐量 | 系统每秒生成的 token 总数(含输入+输出) | tokens/s | 算力利用效率、硬件饱和度 |
| 请求吞吐量 | 系统每秒完成的完整请求数 | requests/s | 业务处理能力、QPS 承载 |
| 并发吞吐量 | 在特定并发数下测得的吞吐量 | tokens/s @ concurrency | 系统在不同负载下的表现曲线 |
需要特别注意的是 tokens/s 与 requests/s 的换算陷阱。假设系统每秒产出 1000 个 token,如果每个请求平均输出 500 token,则每秒完成 2 个请求;但如果请求平均输出 50 token,则每秒可完成 20 个请求。两者差异巨大,但 token 吞吐量完全相同。因此,在汇报性能时必须同时标注两个指标,并说明平均输入/输出长度,否则数据毫无意义。
3.3 性能基准测试方法
度量吞吐量不能靠“感觉”,必须遵循可重复、可对比的基准测试流程。以下是 vLLM 场景下推荐的标准方法:
3.3.1 测试数据集构建
使用真实业务分布的数据集,或使用 ShareGPT、Alpaca 等公开数据集。关键是要控制输入长度分布和输出长度分布。vLLM 官方提供了 benchmark_serving.py 脚本,支持通过参数模拟不同请求模式:
# 使用 vLLM 内置压测脚本,模拟真实请求分布
python benchmark_serving.py \
--backend vllm \
--model meta-llama/Llama-3-8B-Instruct \
--tokenizer meta-llama/Llama-3-8B-Instruct \
--dataset-name sharegpt \
--dataset-path ./ShareGPT_V3_unfiltered_cleaned_split.json \
--num-prompts 500 \
--request-rate 10 \
--max-concurrency 32 \
--output-len 512
其中 --num-prompts 控制总请求数,--request-rate 控制请求到达速率(设为无穷大表示全并发灌入),--max-concurrency 控制客户端最大并发连接数。
3.3.2 测试执行与数据采集
执行测试时,需要同时监控服务端日志和客户端统计。vLLM 的 benchmark 脚本会输出如下关键结果:
Successful requests: 500
Benchmark duration: 128.32s
Total input tokens: 152340
Total generated tokens: 245890
Request throughput: 3.90 requests/s
Output token throughput: 1916.34 tokens/s
Total token throughput: 3103.55 tokens/s
Mean TTFT: 45.2ms
Mean TPOT: 18.7ms
注意:Total token throughput 是输入+输出 token 的总和,而 Output token throughput 仅统计生成部分。在对比不同系统时,务必明确使用的是哪个口径。
3.3.3 并发扫描(Concurrency Sweep)
单点测试无法反映系统全貌。正确做法是进行并发扫描:从并发数 1 开始,按 1、2、4、8、16、32、64、128 逐步增加,记录每个并发点下的吞吐量。将结果绘制成曲线,可以清晰看到系统的“拐点”——即吞吐量不再随并发增加而提升的饱和点。这个拐点通常就是系统的最佳工作负载区间。
3.4 案例:一次完整的吞吐量基准测试
假设我们在一台 8×A100 80GB 的服务器上部署了 Llama-3-70B-Instruct 模型,使用 vLLM 0.6.0 版本,张量并行度为 8。我们希望通过基准测试回答:系统最大能支撑多少 QPS?每 token 成本是多少?
测试步骤如下:
- 准备数据集:从生产日志中随机抽取 2000 条真实请求,统计得到平均输入长度 850 token,平均输出长度 420 token。
- 预热:先发送 50 个请求预热 CUDA 内核和显存分配器,避免冷启动影响。
- 并发扫描:分别在并发 1、4、8、16、32、64、128、256 下各运行 3 分钟,取稳定段数据。
- 记录结果:得到如下关键数据:
| 并发数 | 请求吞吐 (req/s) | 输出吞吐 (tokens/s) | 平均 TTFT (ms) | 平均 TPOT (ms) |
|---|---|---|---|---|
| 1 | 0.8 | 336 | 180 | 42 |
| 16 | 8.2 | 3444 | 210 | 48 |
| 64 | 22.5 | 9450 | 350 | 65 |
| 128 | 28.1 | 11802 | 620 | 89 |
| 256 | 27.8 | 11676 | 1150 | 142 |
从数据可以看到:并发从 128 增加到 256 时,吞吐量不再上升反而微降,且 TTFT 和 TPOT 显著恶化。这说明系统的最佳并发点约为 128,此时吞吐量约 28 req/s,输出 token 吞吐约 11800 tokens/s。超过该点后,排队延迟和调度开销开始主导,系统进入过载区。
3.5 吞吐量度量的常见误区与复盘
在长期性能调优实践中,以下误区反复出现,值得专门复盘:
- 误区一:只测 requests/s,忽略 token 长度分布。 两个系统都报 10 req/s,但一个平均输出 2000 token,另一个平均输出 100 token,实际算力消耗相差 20 倍。正确做法是同时报告
req/s和output tokens/s,并附带平均输入/输出长度。 - 误区二:用单条 prompt 反复压测。 如果所有请求都是同一个短 prompt,KV Cache 的局部性会虚高吞吐量。真实场景中请求长度高度可变,必须使用多样化的数据集。
- 误区三:忽略预热和稳态。 刚启动时 CUDA 图捕获、显存分配尚未稳定,前几十个请求的数据不可信。应丢弃前 10% 的预热数据,只统计稳态段。
- 误区四:把吞吐量当作唯一指标。 吞吐量提升往往伴随延迟上升。在调优时,应设定一个延迟 SLO(如 TTFT < 800ms,TPOT < 100ms),然后在该约束下最大化吞吐量,而不是无约束地追求吞吐。
吞吐量度量是性能工程的“仪表盘”。没有准确的度量,后续的 PagedAttention 调优、Continuous Batching 参数调整、KV Cache 量化等操作都将失去方向。掌握本节的概念与基准方法后,下一节我们将深入 vLLM 的调度引擎,剖析吞吐量瓶颈产生的底层机制。