Skip to content

13. 部署、压测与排错 ​

本篇导读 ​

最后让小林的资料助手接受检验:回答是否正确,一人使用是否流畅,32 位同事同时提问会怎样?本篇把前面的直觉变成一套实验顺序:固定基线、分段计时、每次改变一个因素,再按现象定位原因。读完应能留下可重复的记录,并解释一次性能变化。

先画出等待,再选择实验 ​

同一请求的客户端计时与服务端分段定位

第 2 篇区分了读输入和逐步写输出;现在再把排队与传输加进来。小林看到“第一段很慢”,可能是队伍长,也可能是文档长;“写到一半停住”则要检查逐步生成期间的干扰。先给等待找到位置,才知道该调哪一个开关。

可用的起点是同一台 GPU、一个固定 checkpoint、一套固定请求,分别运行 vLLM 与 SGLang,再逐项改变缓存、并发和精度。不要同时修改模型、模板和多个优化开关,否则很难解释结果。

本章命令是学习模板,未代表任何 GPU 上的已完成跑分。可先在没有 GPU 的机器上使用前文交互理解预算,再到匹配模型支持条件的设备执行真实实验。

建立可复现基线 ​

先按 vLLM 或 SGLang 的最小命令启动一个服务,二选一占用 8000 端口。另一个框架使用独立虚拟环境,切换时先停止旧服务或更换端口。用启动日志确认版本、模型、dtype、后端、上下文与服务就绪。

bash
curl --fail http://127.0.0.1:8000/v1/models

curl --fail http://127.0.0.1:8000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "Qwen/Qwen3-0.6B",
    "messages": [{"role":"user","content":"用三句话解释 KV cache。"}],
    "max_tokens": 256,
    "temperature": 0,
    "stream": true
  }'

先检查回答是否完整,是否有 EOS、长度截断和预期的 reasoning/content 字段。Qwen3 等模型的 thinking 配置会明显改变输出长度,跨引擎对比时必须一致。不要只按看到的正文估算输出工作量。

实验记录模板
yaml
model: 模型标识与revision
tokenizer_template: 版本与模板
engine: 名称与精确版本
image_digest: 使用容器时记录
device: GPU型号、数量、显存、互联
software: 驱动、CUDA或ROCm、kernel后端
precision: 权重、激活、KV dtype
limits: 上下文、序列数、token预算
generation: thinking、温度、top_p、max_tokens、stop
cache: 冷热状态、前缀规则、路由
load: 输入输出长度分布、并发或到达率
quality: 固定样本、成功、截断与错误
metrics: TTFT、E2E、chunk间隔、服务端token统计

测首个内容与流式间隔 ​

配套的客户端脚本只使用 Python 标准库,向指定的兼容接口发送请求,记录首个正文片段、完整耗时、chunk 间隔、结束原因与服务报告的 token 数。它按 delta.content 定义首个有效结果,reasoning-only 流不会被误记成正文 TTFT。

下载 measure_stream.py

bash
python measure_stream.py \
  --url http://127.0.0.1:8000/v1/chat/completions \
  --model Qwen/Qwen3-0.6B \
  --prompt '用三句话解释 KV cache。' \
  --max-tokens 256 --requests 8 --concurrency 2

这是固定并发的闭环诊断客户端,适合建立基线,不是完整生产压测器。运行前先核对地址和模型。若接口需要认证,通过 INFERENCE_API_KEY 环境变量提供;脚本不会输出密钥。它不自行修改 thinking、模板、缓存和服务端配置。

没有正文、发生 HTTP/流错误或缺少正常结束原因都会单独标记;length 截断也会记录。chunk 可含多个 token,因此脚本的间隔不是精确 ITL。输出 token 以服务 usage 为准,不用字符数冒充。

五组有解释力的实验 ​

实验固定项唯一主要变化应观察什么
单请求基线模型、精度、长度、采样引擎正确性、冷启动、TTFT、生成阶段
冷热前缀长文档、后置问题首次与重复前缀命中 token、prefill、TTFT
并发扫描同一长度分布1、2、4、8…并发吞吐转折、p95/p99、抢占
混合输入同一请求集合长短到达顺序、chunk 配置正在 decode 请求的停顿
精度或推测相同质量集与负载单个优化开关质量、显存、TTFT、goodput

做缓存实验时,先重启或按引擎提供的安全接口清理,再分别测试冷缓存、完全重复、共享文档换问题、打散路由。只换最后一个问题不等于冷请求;给一个框架热缓存、另一个冷缓存也不公平。

做并发扫描时,在错误率或尾延迟明显失控后停止继续加压,先定位资源边界。这个转折本身是容量信息,而非要用更大队列掩盖的现象。

两种到达模型 ​

固定并发模式每结束一个请求再补一个。服务变慢时,发生器也会自然减速,适合观察某并发下的完成能力,却可能隐藏真实突发排队。

固定到达率模式按目标 RPS 或真实时间戳持续发送。它能暴露队列失稳和尾延迟,需同时记录发生器是否按时发出请求,以及被限流、超时与重试的数量。开放负载测试应使用能记录计划与实际发送时间的工具,不能用闭环客户端的结果冒充。

稳定状态下,Little 定律可辅助核对:系统内平均请求数 = 平均到达率 × 平均停留时间。这需要一致的统计边界和稳定性,不能替代尾延迟与突发实验。

看 goodput,而不只是吞吐 ​

输出吞吐是成功输出 token / 墙钟时间,请求吞吐是请求数 / 时间。短输出会显著提高请求吞吐,不能直接推断长文本能力。Goodput 进一步只计算满足质量、成功和延迟目标的有效工作。

比较图表应至少保留这些列:实际输入输出长度、成功数、截断数、错误数、TTFT 分位数、E2E 分位数、服务端 token 吞吐、峰值显存和成本。样本少时不要过度解释 p99;报告样本量、预热与测量窗口。

从现象缩小范围 ​

现象先分解什么下一步验证
第一次很慢,之后正常模型加载、编译、图捕获、冷缓存分别记录冷启动与热请求
TTFT 高、decode 正常模板、分词、队列、prefill、encoder比较短入和长入,冷/热前缀
生成中间停顿长 prefill、batch、通信、CPU 空隙长短混合,trace 与每步分布
高并发吞吐下降KV、抢占、重算、CPU 和网络降并发,核对缓存压力与等待
量化后反而变慢kernel、shape、反量化与布局同 shape 对比后端与精度
多卡变慢互联、TP通信、流水气泡单卡可容纳时建立通信对照
结构化输出异常Schema支持、状态、截断、推测组合先关推测,固定短schema,再逐项恢复
语音用户体感慢端点、ASR收尾、文本块、TTS与播放用首音频关键路径逐段打点

日志先给时间边界和请求 ID,再关联引擎状态、GPU trace 与客户端观测。只看 GPU 利用率或一条“模型耗时”,无法区分排队、执行和传输。

学习与升级的达成标志 ​

能够独立解释:长上下文为什么仍让 decode 变慢,分页与前缀命中为什么不同,增大 batch 为什么可能伤害尾延迟,INT4 为什么不一定快,推测为什么会回滚,以及语音首音频为什么不是文本 TTFT。

随后应能完成一次双引擎同条件实验,保留可复现记录,并把每项性能差异定位到缓存、调度、kernel、精度、输出长度或其他明确机制。上线升级还要覆盖取消、错误、超时、长输入、结构化输出和实际使用的功能组合;确认质量与延迟后,再逐步放量并保留回退版本。

本篇回顾 ​

  • 用固定模型、模板、请求和设备建立基线,每轮只改变一个主要因素。
  • 客户端体验与服务端分段必须对齐;网络 chunk 不是 token,GPU 利用率也不是完整诊断。
  • 选择满足质量和延迟要求的有效容量,保留失败样本、配置和回退版本。

回看整条路线:小林的一句提问,先变成 token,再消耗权重、状态和计算资源;引擎负责组织这些工作,部署方式与语音链路继续放大系统问题。现在可以回到系列总览,按实际瓶颈重读对应章节,并用实验检验理解。

别急,先让缓存热一下。