框架长线学习-11-性能剖析方法论:吞吐量、延迟、显存利用率权衡

​① 这篇讲什么 ​------怎么量化地衡量和优化推理服务:吞吐、延迟、显存三者怎么权衡,看哪些指标、调哪些参数。

​② 它的核心观点对不对 ​------​**"不可能三角"的思路是对的​,而且​正好解释了你之前那个疑问**​:为什么 P 节点 token 预算大、并发小,D 节点预算小、并发大------那就是这套权衡在 PD 分离里的具体落地。

​③ ⚠️ 但它的 API 和指标名基本是编的 ​------python -m vllm.entrypoints.benchmark、VLLM_PROFILING_ENABLED=1、还有那些 vllm_xxx 指标名(少了 vllm: 前缀、好几个根本不存在)​都别当真 ​。而且它是 ​NVIDIA 视角​,你是昇腾 NPU,工具名要换。

下面从零讲。


一、先建立三个指标的直觉(餐厅版)

指标 是什么 餐厅比喻 谁最关心
吞吐量 Throughput​ 单位时间总共吐多少 token(Tokens/s) 一小时总共出多少道菜​ 成本/离线批处理
延迟 Latency​ 单个请求多久完成 一桌客人等多久上菜​ 交互/聊天体验
显存利用率​ 显存用了多少(决定能装多少 KV) 仓库堆得多满​ 能不能同时接更多客

延迟拆成三截(这个必须分清)

sql 复制代码
端到端延迟 = TTFT + 生成token数 × TPOT

TTFT(Time To First Token)  = 首字延迟:请求进来到吐第一个字
                              ← 主要由 prefill 决定(读题要多久)

TPOT(Time Per Output Token)= 字间延迟:两个字之间的间隔
                              ← 主要由 decode 决定(每吐一个字要多久)

E2E(端到端)                = 整个请求从发到收完

​这直接接上你刚学的​:prefill 慢 → TTFT 高;decode 慢(搬运权重)→ TPOT 高。


二、⭐ 为什么三者冲突("不可能三角")

这是本章的骨架,我用最直白的方式讲。

冲突 1:吞吐 vs 延迟 ------ 核心是"一次算多少"

复制代码
大 batch(一次算很多):
  ✅ 算力吃饱 → 吞吐高
  ❌ 但每轮算得多 → 单步变慢;请求还可能要等凑批 → 延迟高

小 batch(一次算一点):
  ✅ 每步快、不用等 → 延迟低
  ❌ 但算力闲置 → 吞吐低、单 token 成本高

​打比方​:

markdown 复制代码
大巴车(大 batch):一趟拉 50 人,人均成本低(吞吐高)
                  但你要等凑满 50 人才发车(延迟高)

出租车(小 batch):来一个走一个,快(延迟低)
                  但一趟只拉 1 人,人均成本高(吞吐低)

​vLLM 的连续批缓和了这对矛盾 ​------因为它允许"有人下车、有人上车",新请求不用等整批跑完。但​权衡的本质没变​。

冲突 2:显存利用率 vs 其他两个

复制代码
显存用得越满 → KV Cache 越大 → 能装更多并发 → 吞吐↑、排队↓
但太满 → 没有缓冲 → 突发长请求一来就 OOM → 服务直接崩(吞吐归零)

​**所以显存不是"越高越好",而是"在留够安全余量的前提下尽量高"**​------你之前学到的 gpu_memory_utilization 就是这条水位线。

这张表是全文最实用的(业务对号入座)

业务类型 首要目标 调优方向 牺牲什么
​成本敏感​(离线批处理) 单 token 成本最低 拉满吞吐(大 batch) 延迟(尤其 TTFT)
交互聊天​ 用户感觉快 压 TTFT 和 TPOT(小 batch) 吞吐、硬件利用率
长文本/RAG​ 撑住长上下文、高并发 优化显存 + 开前缀缓存 极致的吞吐或延迟

三、怎么测量(工具要换成昇腾的)

文档说的(NVIDIA 视角,你要替换)

文档写的 昇腾上你该用
nvidia-smi **npu-smi**​
dcgm(看带宽) msprof / Ascend PyTorch Profiler​
Nsight(看时间线) MindStudio Insight​
"GPU 算力利用率" AI Core 利用率​
"GPU 显存" ​NPU 显存(HBM)​​

/metrics 端点:真实存在,但指标名文档写错了

curl http://localhost:8000/metrics 这个​是真的 ​(vLLM 暴露 Prometheus 指标)。但文档给的指标名几乎全错------​真实的是带 vllm: 前缀的​:

文档写的(❌) 真实的(✅)
vllm_num_requests_waiting **vllm:num_requests_waiting**​
vllm_num_requests_running **vllm:num_requests_running**​
vllm_request_latency_seconds **vllm:e2e_request_latency_seconds**​
vllm_ttft_seconds **vllm:time_to_first_token_seconds**​
vllm_gpu_cache_usage_ratio **vllm:gpu_cache_usage_perc**​
vllm_num_generated_tokens_total **vllm:num_generation_tokens_total**​
vllm_batch_size ❌ 不存在
vllm_num_blocks_in_gpu ❌ 不存在

​另外几个你以后会用到的真实指标​:

复制代码
前缀缓存命中率:
  vllm:prefix_cache_queries_total
  vllm:prefix_cache_hits_total     ← 命中数 / 查询数 = 命中率

推测解码接受率:
  vllm:spec_decode_num_draft_tokens_total
  vllm:spec_decode_num_accepted_tokens_total   ← 接受/草稿 = 接受率

​这两个正好能回答你之前悬着的两个问题​:前缀缓存到底命中了多少、MTP 猜的到底接受了多少。

压测命令:文档那个是编的

复制代码
❌ python -m vllm.entrypoints.benchmark ...(不存在这个模块)
✅ vllm bench serve ...      (在线服务压测,测 TTFT/TPOT/吞吐)
✅ vllm bench throughput ... (离线吞吐)

昇腾上一般配合 AISBench​ 做压测。


四、调优旋钮(这些全学过,现在串成体系)

文档第 3 步列的这些参数​方向是对的 ​,而且​每个你都已经认识​:

参数 往哪调 影响
**max_num_batched_tokens**​ ⭐最重要的一个​ 每轮 token 预算。调大→吞吐↑但单步变慢(延迟↑);调小→反之
max_num_seqs 并发天花板 调大→并发↑(吞吐↑),但调度开销和延迟↑
gpu_memory_utilization 显存水位 决定给 KV Cache 留多少→决定并发上限
block_size 货架大小 小块→对齐容易、前缀命中率高;大块→管理开销小
分块预填充 治长 prompt 主要降 TTFT​
前缀缓存 相同前缀 ​降 TTFT、省显存​,对 TPOT 无帮助
推测解码(MTP) 一次吐多字 ​降 TPOT、提吞吐​,但算力打满时收益下降

⭐ 这正好解释了我的 PD 分离配置

回看那套架构(抽象讲,不引具体值):

复制代码
Prefill 节点:
  token 预算【大】+ 并发【小】
  → 因为 prefill 是重活,一次算一大块才能把算力吃满
  → 追求:单请求的 prefill 效率(吞吐)

Decode 节点:
  token 预算【小】+ 并发【大】
  → 因为 decode 每步只算 1~2 个 token(很轻),必须堆大并发喂饱算力
  → 预算小是为了让每步都很快(TPOT 低)
  → 追求:低延迟 + 高并发吞吐

​这就是"吞吐 vs 延迟"权衡在两个阶段上的不同落点​------两阶段脾气不同,所以最优解也不同。这正是 PD 分离能拿到收益的根本原因。


五、"先找瓶颈,再调参"(文档步骤一,这条最实用)

文档说调优第一步是​判断瓶颈在哪​------这跟你之前学的 profiling 完全对上。三类瓶颈:

瓶颈 怎么看出来 该往哪调
​算力瓶颈​(Compute-bound) AI Core 利用率接近 100%,带宽不高 增大 batch、上量化/稀疏减少计算量
​带宽瓶颈​(Memory-bound) 带宽打满、算力空闲 量化(少搬权重)、算子融合、推测解码
显存容量瓶颈​ 显存接近满、请求排队多 调gpu_memory_utilization、缩短max_model_len、开前缀缓存

​之前学的"搬运 vs 计算"那个判断,就是这里的第一步​------先定位是哪种瓶颈,再决定调什么。乱调参数(比如明明是带宽瓶颈却去加 batch)往往没用。

​⚠️ ​:​下发瓶颈(Host-bound)​------NPU 大片空等在等 CPU 下命令。判据是时间线上 NPU 有空白。解法是​开图模式 ​( D 节点用 FULL_DECODE_ONLY 就是在治它)。


六、挂知识树


arduino 复制代码
vLLM 知识体系
├─ 【前传】模型怎么装进来
├─ 【主线】怎么跑得快
│   ├─ FlashAttention / PagedAttention
│   ├─ Scheduler / 连续批
│   ├─ 分块预填充 / 前缀缓存
│   ├─ 图模式 / 推测解码
│   └─ 【本篇】性能剖析方法论 ← 横向统领上面所有技术
│       ├─ 三个指标:吞吐 / 延迟(TTFT·TPOT) / 显存
│       ├─ 不可能三角:大batch↔低延迟、显存↔OOM风险
│       └─ 方法:先定位瓶颈(算力/带宽/容量/下发)→ 再调对应旋钮
├─ 【观测】profiling(已学:torch profiler / msprof / MindStudio Insight)
│       └─ 本篇是"怎么用这些工具做决策"的方法论
├─ 【外传一】改结构(MoE / 多模态)
└─ 【外传二】改精度(W8A8 / FP8)

​它是横向的一层 ​------不新增技术,而是教​在众多技术里怎么选、怎么调​。前面学的每一个优化技术,都是这套方法论里的一个"旋钮"。


一句话总结

这篇讲的是​**"怎么科学地调 vLLM"​:三个指标(​ 吞吐 / 延迟 / 显存​)构成不可能三角------​大 batch 换吞吐但伤延迟,显存用得越满并发越高但 OOM 风险越大**​。

​方法就三步 ​:① 先用 profiling + /metrics​定位瓶颈 ​(算力 / 带宽 / 容量 / 下发);② 按业务目标(成本型 / 交互型 / 长文型)​定方向 ​;③ 调对应的旋钮------​**max_num_batched_tokens 是最核心的那个**​,其余是 max_num_seqs、显存水位、分块预填充、前缀缓存、推测解码。

​PD 分离配置就是这套权衡的落地​:prefill 侧预算大并发小(榨吞吐),decode 侧预算小并发大(压延迟)。


​立刻能做的实操建议 ​:下次压测时,除了看总吞吐,​专门抓三个数字 ​------vllm:time_to_first_token_seconds(TTFT)、vllm:time_per_output_token_seconds(TPOT)、vllm:gpu_cache_usage_perc(KV 水位)。这三个数一出来,立刻判断当前卡在哪个阶段、该往哪个方向调,不用再凭感觉试参数。

相关推荐
rich_Chan.1 小时前
【2026 实操】bl video edit 教程:已拍素材改风格换颜色全流程,含输出规格、角标自查
人工智能·ai·云计算·aigc
AI日报派送佬1 小时前
2026年11月01日AI行业日报|可灵AI198亿融资落地、DeepSeek开源超级智能体、Anthropic取消Ultra时长限制
人工智能·开源·openai·deepseek·anthropic·ai日报
程序员cxuan2 小时前
通了个宵,终于把 Super-Agent 架构玩明白了
人工智能·后端·程序员
张张123y2 小时前
后端第十六课:Multi-Agent 多智能体(让多个 Agent 分工协作)
大数据·数据库·人工智能·python·deepseek·dsh·agent 框架
释厄6232 小时前
同性线套娃律——圆面积必然=1+2+...+n 的求和·圆心不与 π 相关
java·人工智能·算法·1024程序员节
u1301302 小时前
GitHub 热榜项目:日榜(2026-11-01)
人工智能·github
QXWZ_IA2 小时前
边坡深部位移数据异常的甄别流程
人工智能
原子延迟2 小时前
Smallpdf免费次数用完了(2026),4款工具避坑
图像处理·人工智能·计算机视觉
code2cat2 小时前
【随笔】vLLM前缀缓存:相同的开头怎样少算一次
人工智能·python·缓存·vllm