大模型推理成本优化实战:vLLM 与 KV Cache 调优指南

大模型真正进入生产环境以后,最容易被低估的问题往往不是“模型能不能跑起来”,而是“每生成一个 Token 到底要花多少钱”。


在实验环境中,单次请求跑得快、显存占得满,并不意味着线上服务具有良好的经济性。生产环境面对的是持续到来的并发请求、长度差异巨大的 Prompt、不断增长的 KV Cache,以及 TTFT(Time To First Token)、TPOT(Time Per Output Token)、吞吐量和显存利用率之间复杂的权衡。


尤其对于长上下文和高并发场景,模型权重本身反而不一定是第一瓶颈。随着请求数量和上下文长度增长,KV Cache 会迅速占据大量 GPU 显存,并进一步限制 batch size、并发数和整体吞吐。


因此,大模型推理成本优化不能简单理解为“把模型从 FP16 换成 INT4”。


真正有效的优化通常来自一个完整的推理栈:

模型量化 + PagedAttention + KV Cache 管理 + Continuous Batching + Prefix Caching + Kernel 优化 + 并发调度 + 硬件利用率优化

vLLM 正是围绕这一类问题设计的高性能 LLM 推理框架。当前官方文档已经将 PagedAttention、Continuous Batching、Chunked Prefill、Prefix Caching、量化、FlashAttention/FlashInfer、CUDA Graph、Speculative Decoding 等能力集成到统一推理框架中。([vLLM][1])


本文从 Transformer 推理原理出发,重点分析 KV Cache 为什么会成为成本瓶颈,以及如何使用 vLLM 对推理服务进行系统性优化。


一、先理解一个事实:LLM 推理为什么昂贵

Transformer 的自回归生成过程可以简单理解为:

输入 Prompt
    │
    ▼
Tokenizer
    │
    ▼
Prefill
    │
    ├── 计算整个 Prompt
    ├── 生成 K/V
    └── 保存到 KV Cache
    │
    ▼
Decode
    │
    ├── 读取历史 KV
    ├── 计算下一个 Token
    ├── 生成新的 K/V
    └── 写入 KV Cache
    │
    ▼
下一个 Token

其中可以把推理过程分成两个阶段:

1. Prefill

Prefill 负责处理用户提交的 Prompt。


例如:

System Prompt       200 tokens
User Context        5000 tokens
Question             200 tokens
--------------------------------
Total               5400 tokens

模型需要一次性处理这 5400 个 Token。


Prefill 通常具有较高的计算并行度,因此更容易表现为计算密集型任务。

2. Decode

生成阶段则完全不同。


假设模型需要生成 500 Token:

Token 1
   ↓
Token 2
   ↓
Token 3
   ↓
...
Token 500

每一步生成下一个 Token 都依赖之前已经生成的内容。


因此 Decode 是典型的自回归过程。


如果每次都重新计算完整上下文,计算量会非常巨大。


KV Cache 的核心价值就在这里:

已经计算过的 Key 和 Value 不需要每个 Decode Step 重新计算。

这也是为什么 KV Cache 是现代 LLM 推理系统的核心资源之一。


二、KV Cache 到底缓存了什么

Transformer Attention 的核心计算可以简化为:

Attention(Q, K, V)
=
softmax(QKᵀ / √d)V

其中:

  • Q:Query

  • K:Key

  • V:Value

在自回归生成过程中,新 Token 会产生新的 Q、K、V。


但是历史 Token 的 K、V 已经计算过。


例如:

Token 1 → K1 V1
Token 2 → K2 V2
Token 3 → K3 V3
Token 4 → K4 V4

生成 Token 5 时:

Q5
 │
 ├── K1
 ├── K2
 ├── K3
 └── K4

V1
V2
V3
V4

因此系统可以直接读取:

[K1,K2,K3,K4]
[V1,V2,V3,V4]

而不需要重新计算历史 Token。


这就是 KV Cache。


2.1 KV Cache 为什么特别吃显存

假设一个模型具有:

Layers = 32
KV Heads = 8
Head Dimension = 128
Datatype = FP16

那么每个 Token 的 KV Cache 大小近似为:

KV bytes/token
=
Layers
× KV Heads
× Head Dimension
× 2(K,V)
× bytes_per_element

代入:

32 × 8 × 128 × 2 × 2
=
131072 bytes

也就是:

128 KB / token

如果一个请求上下文达到 8192 Token:

8192 × 128 KB
≈ 1 GB

注意,这只是一个请求的数量级估算。


如果同时存在:

16 requests

理论上就可能需要接近:

16 GB

的 KV Cache。


所以在长上下文、高并发服务中,真正限制并发能力的往往不是模型权重,而是 KV Cache。


三、为什么传统 KV Cache 管理效率并不高

最早的推理实现通常会为每个请求分配连续显存:

Request A
┌───────────────────────────────┐
│ K/V Token 0 ... Token 4095    │
└───────────────────────────────┘

Request B
┌───────────────────────────────┐
│ K/V Token 0 ... Token 2047    │
└───────────────────────────────┘

问题在于请求长度并不固定。


例如:

Request A = 4096 tokens
Request B = 900 tokens
Request C = 12000 tokens
Request D = 2300 tokens

如果系统按照最大长度预分配,就会产生大量浪费。


如果动态扩容,又容易产生显存碎片。


这和传统操作系统内存分配非常类似:

连续内存
    ↓
动态增长
    ↓
碎片
    ↓
分配困难

这正是 vLLM PagedAttention 要解决的问题。


四、PagedAttention:把 KV Cache 像虚拟内存一样管理

vLLM 的 PagedAttention 借鉴了操作系统虚拟内存和分页机制。


它不再要求一个请求的 KV Cache 必须连续存储,而是把 KV Cache 切成多个固定大小的 Block。


逻辑上:

Request A

Logical KV Cache

[Block 0][Block 1][Block 2][Block 3]

实际 GPU 显存可能是:

Physical GPU Memory

[Block 7]
[Block 2]
[Block 19]
[Block 4]

然后通过 Block Table 建立映射:

Logical Block     Physical Block

Block 0     ───→   Block 7
Block 1     ───→   Block 2
Block 2     ───→   Block 19
Block 3     ───→   Block 4

于是:

逻辑连续
    ↓
物理离散

这就是 PagedAttention 最核心的思想。


4.1 PagedAttention 带来的收益

首先是显存碎片显著降低。


传统模式:

连续分配
AAAAAAAAAAAA
BBBBBB
CCCCCCCC
AAAAAA

容易出现:

Free
Used
Free
Used
Free

而分页方式:

Block Pool

[Free]
[Used]
[Used]
[Free]
[Used]
[Used]
[Free]

请求需要多少 Block 就分配多少。


当请求结束:

Request A
    ↓
释放 Block
    ↓
Block Pool
    ↓
其他 Request 复用

因此 GPU 显存可以更加充分地服务于真实 Token。


PagedAttention 正是 vLLM 的核心内存管理机制之一。([vLLM][1])


五、Continuous Batching:不要等待整个 Batch 结束

传统静态 Batch 的逻辑通常是:

Batch
 ├── Request A
 ├── Request B
 ├── Request C
 └── Request D

全部完成
    ↓
Batch 结束
    ↓
下一批

最大的问题是:

Request A → 100 tokens
Request B → 2000 tokens
Request C → 300 tokens
Request D → 5000 tokens

如果按照最长请求同步执行:

A ──────────┐
B ─────────────────────
C ───────────
D ────────────────────────────────

短请求会被迫等待。


GPU 的计算资源也会因此出现大量浪费。


5.1 Continuous Batching 的思想

Continuous Batching 则把请求生命周期拆开:

Time →

A  █████████
B  █████████████████
C  ██████
D        █████████████
E             ███████

某个请求完成后:

Request A
    ↓
立即退出 Batch
    ↓
Request E
    ↓
立即进入

因此 GPU 在每一个 iteration 中都可以尽可能装载更多可执行请求。


vLLM 官方当前版本将 Continuous Batching、Chunked Prefill、Prefix Caching 等作为核心服务能力。([vLLM][1])


六、vLLM 的完整推理架构

生产环境中的 vLLM 可以理解为:

                   Client
                      │
                      ▼
              ┌───────────────┐
              │ API Gateway   │
              └───────┬───────┘
                      │
                      ▼
              ┌───────────────┐
              │ vLLM Server   │
              └───────┬───────┘
                      │
          ┌───────────┴───────────┐
          ▼                       ▼
   Request Scheduler        KV Cache Manager
          │                       │
          ▼                       ▼
 Continuous Batching        Paged KV Blocks
          │                       │
          └───────────┬───────────┘
                      ▼
                Attention Kernel
                      │
          ┌───────────┴───────────┐
          ▼                       ▼
      FlashAttention          GEMM Kernel
          │                       │
          └───────────┬───────────┘
                      ▼
                     GPU

如果把整个优化过程抽象成一条链:

请求调度
   ↓
Batching
   ↓
KV Cache 分配
   ↓
Attention Kernel
   ↓
GEMM
   ↓
GPU

其中任何一个环节成为瓶颈,整体吞吐都会下降。


七、第一步优化:不要盲目追求最大 Batch

这是生产环境非常容易犯的错误。


很多人看到 GPU 显存还有空间,就认为:

Batch 越大越好。

实际上并不是。


Batch 增大通常意味着:

吞吐 ↑
显存占用 ↑
排队延迟 ↑
TTFT 可能 ↑
调度复杂度 ↑

尤其是长 Prompt 场景。


例如:

Batch = 8
Average Input = 2K


和:

Batch = 8
Average Input = 32K

虽然 Batch 一样,但第二种情况的 KV Cache 压力完全不是一个数量级。


因此真正应该关注的是:

tokens / batch

而不是简单的:

requests / batch


八、max-num-batched-tokens 是关键参数

vLLM 的生产调优不应该只盯着:

max_num_seqs

更重要的是控制每个 iteration 中处理的 Token 数量。


典型启动方式:

vllm serve Qwen/Qwen3-8B \
  --host 0.0.0.0 \
  --port 8000 \
  --gpu-memory-utilization 0.90 \
  --max-num-seqs 128 \
  --max-num-batched-tokens 16384

这里的思路是:

max-num-seqs
    ↓
限制并发请求数量

max-num-batched-tokens
    ↓
限制单次调度 Token 工作量

两者应该联合调节。


九、GPU Memory Utilization 不是越高越好

vLLM 可以利用 GPU 显存的一定比例作为推理资源。


例如:

--gpu-memory-utilization 0.90

很多生产环境喜欢直接:

0.95
0.98

但这样做风险较高。


GPU 显存并不只有模型权重:

GPU Memory
├── Model Weights
├── KV Cache
├── CUDA Runtime
├── Temporary Buffers
├── Activation
├── Kernel Workspace
└── Other Runtime Memory

如果把显存压得过满,可能出现:

OOM

甚至:

偶发 OOM

这种问题比启动时直接 OOM 更难排查。


更合理的方法是:

0.85
 ↓
0.90
 ↓
0.92

逐级压测。


十、KV Cache 优化的核心公式

对于标准 MHA,可以近似表示 KV Cache:

KV Cache Memory
≈
2 × Layers × KV Heads × Head Dim
× Sequence Length
× Batch Size
× Bytes

这里最值得关注的是:

KV Heads

现代模型大量使用 GQA(Grouped Query Attention)或者 MQA(Multi-Query Attention)。


例如:

Query Heads = 32
KV Heads    = 8

那么 KV Cache 的 Head 数量不是 32,而是 8。


理论上,相比:

32 KV Heads

可以把 KV Cache 的这一部分降低到:

8 / 32 = 25%

这也是 GQA 对推理系统非常重要的原因。


TensorRT-LLM 的 KV Cache 文档同样明确利用 MHA、MQA 和 GQA 的不同 KV Head 结构来优化 KV Cache 内存。([NVIDIA GitHub][2])


十一、Prefix Caching:重复 Prompt 不要重复计算

很多企业应用存在高度重复的 Prompt。


例如:

System Prompt

+
企业知识库规则

+
工具定义

+
Agent Instructions

+
用户问题

其中前面几千 Token 可能完全相同。


如果每次请求都重新 Prefill:

Request 1
System Prompt → Compute

Request 2
System Prompt → Compute

Request 3
System Prompt → Compute

Request 4
System Prompt → Compute

这是明显的浪费。


Prefix Caching 的思想就是:

第一次请求

Prefix
  ↓
Compute
  ↓
KV Cache
  ↓
保存


后续请求

Prefix
  ↓
匹配
  ↓
直接复用 KV
  ↓
只计算新增内容

因此:

TTFT ↓
GPU Compute ↓
成本 ↓

对于以下场景尤其有效:

  • Chatbot

  • Agent

  • RAG

  • 企业知识库

  • 固定 System Prompt

  • 大量工具定义

  • 多轮对话


十二、Chunked Prefill:解决长 Prompt 阻塞 Decode

一个非常典型的问题:

当前有 100 个 Decode 请求

突然来了:

一个 100K Token Prompt

如果直接进行完整 Prefill:

100K Token Prefill
       ↓
GPU 被长时间占用
       ↓
Decode 请求等待
       ↓
用户感知延迟暴涨

Chunked Prefill 可以把长 Prompt 拆成多个 Chunk:

100K Prompt

[Chunk 1]
[Chunk 2]
[Chunk 3]
[Chunk 4]
...

然后让 Prefill 和 Decode 更合理地共享 GPU 计算资源。


例如:

Iteration 1
Decode + Prefill Chunk

Iteration 2
Decode + Prefill Chunk

Iteration 3
Decode + Prefill Chunk

这实际上是在解决:

吞吐与交互延迟之间的调度问题。

所以生产系统不能只看 tokens/s,还必须同时观察:

TTFT
ITL / TPOT
P99 Latency
Queue Time
Throughput


十三、量化:降低模型权重成本

如果模型采用 BF16:

2 bytes / parameter

那么一个 70B 模型,仅权重理论存储量大约:

70B × 2
≈ 140 GB

还没有计算:

KV Cache
Runtime Memory
Temporary Buffer

如果使用 INT4:

70B × 0.5
≈ 35 GB

这意味着同样的 GPU 集群可能运行更大的模型,或者运行更多副本。


常见量化方案包括:

FP8
INT8
INT4
GPTQ
AWQ

当前 vLLM 官方文档已经支持包括 FP8、MXFP8/MXFP4、NVFP4、INT8、INT4、GPTQ、AWQ、GGUF 等多种量化方式。([vLLM][1])


十四、为什么不能简单认为 INT4 一定比 FP8 快

这是推理优化中非常重要的一点。


量化影响的不只是显存。


还涉及:

Memory Bandwidth
Compute Throughput
Kernel Efficiency
Dequantization
Tensor Core Utilization
Accuracy

例如:

FP8

在现代 NVIDIA GPU 上往往具有非常成熟的硬件支持。


而某些 INT4 格式虽然理论上更省显存:

4 bit

但是如果对应 Kernel 不够高效:

Memory ↓
但
Kernel efficiency ↓

最终吞吐不一定更好。


所以量化选择应该通过 Benchmark 决定,而不是根据 Bit 数字大小决定。


十五、KV Cache 本身也可以量化

这是很多部署团队容易忽略的一层。


传统情况下:

Model Weight → Quantized
KV Cache     → FP16 / BF16

实际上 KV Cache 也可以进行低精度存储。


例如:

FP16 KV Cache
↓
FP8 KV Cache

理论上:

2 bytes
↓
1 byte

KV Cache 存储量约减少一半。


对于长上下文、高并发场景,这个收益非常直接。


NVIDIA TensorRT-LLM 当前文档明确支持 FP8 KV Cache,并给出了量化 KV Cache 后吞吐提升的 benchmark 示例;但官方也特别指出,KV Cache 量化需要关注输出质量退化风险。([NVIDIA GitHub][3])


因此:

权重量化解决“模型装不下”的问题,KV Cache 量化解决“并发撑不住”的问题。

两者不是一回事。


十六、vLLM 中如何做 KV Cache 调优

实际部署时,可以先从一个相对保守的配置开始:

vllm serve Qwen/Qwen3-8B \
  --host 0.0.0.0 \
  --port 8000 \
  --dtype bfloat16 \
  --gpu-memory-utilization 0.90 \
  --max-num-seqs 64 \
  --max-num-batched-tokens 8192

然后根据压测结果调整。


典型调优路径:

Step 1
固定模型
    ↓
Step 2
固定 Prompt
    ↓
Step 3
固定输出长度
    ↓
Step 4
测试并发
    ↓
Step 5
观察 TTFT / TPOT
    ↓
Step 6
调整 max-num-seqs
    ↓
Step 7
调整 max-num-batched-tokens
    ↓
Step 8
调整 GPU Memory
    ↓
Step 9
启用 Prefix Cache
    ↓
Step 10
测试量化

不要一次修改十个参数。


否则最后根本无法判断:

到底是哪一个参数带来了提升?


十七、生产环境推荐的服务架构

如果是企业级 RAG / Agent 服务,不建议直接把 vLLM 暴露给公网。


更合理的架构是:

                    Internet
                       │
                       ▼
               ┌───────────────┐
               │ API Gateway   │
               └───────┬───────┘
                       │
            ┌──────────┴──────────┐
            ▼                     ▼
      Rate Limiter          Auth / Billing
            │                     │
            └──────────┬──────────┘
                       ▼
                ┌─────────────┐
                │ LoadBalance │
                └──────┬──────┘
                       │
          ┌────────────┼────────────┐
          ▼            ▼            ▼
       vLLM-1        vLLM-2       vLLM-3
          │            │            │
          ▼            ▼            ▼
        GPU          GPU          GPU

进一步可以把:

Prefill
Decode

进行资源隔离。


高级架构可以设计为:

                API Gateway
                     │
                     ▼
              Request Router
                     │
          ┌──────────┴──────────┐
          ▼                     ▼
    Prefill Cluster        Decode Cluster
          │                     │
       GPU Pool              GPU Pool

vLLM 当前也已经提供 Prefill、Decode、Encode 等阶段解耦相关能力,为这类架构提供了基础。([vLLM][1])


十八、RAG 场景下,真正应该优化的是 Token

RAG 系统经常存在一个误区:

检索更多文档,回答一定更准确。

实际上:

TopK = 20

并不一定比:

TopK = 5

更好。


假设:

每个 Chunk = 700 tokens

TopK = 20

700 × 20
=
14000 tokens

再加:

System Prompt
Conversation History
Question
Instructions

很容易达到:

16K ~ 20K tokens

如果每天:

100万请求

那么输入 Token 成本会非常可观。


因此 RAG 优化应该首先从:

Retrieval
 ↓
Rerank
 ↓
Compression
 ↓
Context

开始。


例如:

召回 20
   ↓
Rerank
   ↓
保留 6
   ↓
Context Compression
   ↓
最终 3K tokens

这往往比单纯优化 GPU 更便宜。


十九、Agent 场景更加依赖 Prefix Cache

Agent 的 Prompt 通常具有高度重复结构:

System Prompt

+
Tools

+
Rules

+
Memory

+
Previous Messages

+
Current Task

其中:

System Prompt
Tools
Rules

通常长期稳定。


可以将 Prompt 设计成:

[Stable Prefix]
System
Tools
Rules
Policies

[Dynamic Suffix]
Memory
User Input
Current State

这样 Prefix Cache 才更容易命中。


这是一个很重要的工程原则:

缓存优化不仅是推理框架问题,也是 Prompt Architecture 问题。

如果每一次请求都改变 Prompt 的前半部分,那么 Prefix Cache 的收益自然有限。


二十、不要忽略输出 Token

很多团队优化时只盯着输入。


实际上:

总 Token
=
Input Tokens

+
Output Tokens

如果一个 Agent:

Input = 5K
Output = 5K

那么输出长度已经占到一半。


如果模型每次都生成:

大量解释
JSON
中间推理
工具描述

成本会明显增加。


因此应该设置合理的:

max_tokens

同时从 Prompt 层面减少不必要输出。


例如:

返回 JSON
不要解释
字段固定

相比:

请详细分析这个问题,并从多个角度进行解释……

更容易控制输出长度。


二十一、Speculative Decoding:进一步降低 Decode 延迟

当 Decode 成为主要瓶颈时,可以考虑 Speculative Decoding。


基本思想:

Small Draft Model
       │
       ▼
生成多个候选 Token
       │
       ▼
Large Target Model
       │
       ▼
并行验证

例如:

Draft Model

A B C D E
─────────►

Target Model

验证 A B C D E
──────────────►

如果候选 Token 大量被接受:

一次 Target Forward
=
多个 Token

那么有效 Decode 速度就可以提高。


vLLM 当前官方能力列表已经包含多种 Speculative Decoding 方法,包括 n-gram、EAGLE、DFlash 等。([vLLM][1])


不过 Speculative Decoding 并不是所有模型、所有工作负载都有效。


如果:

Draft Acceptance Rate 很低

那么额外的 Draft 计算反而可能浪费资源。


所以依然应该 Benchmark。


二十二、真正有效的 Benchmark 怎么做

不要使用:

单请求
短 Prompt
短输出

测试出来的结果作为生产性能。


应该至少建立四类测试。

Case A:短上下文

Input = 512
Output = 256
Concurrency = 1 / 8 / 32 / 64

Case B:中等上下文

Input = 4K
Output = 512
Concurrency = 8 / 32 / 64

Case C:长上下文

Input = 16K
Output = 1K
Concurrency = 8 / 16 / 32

Case D:真实业务

RAG
Agent
Tool Calling
Multi-turn Chat

最终比较:

指标

基线

优化后

TTFT P50



TTFT P95



TPOT



P95 Latency



Output tok/s



Total tok/s



GPU Utilization



KV Cache Usage



Requests/s



Cost / 1M tokens




二十三、成本应该按照 Token 计算,而不是只看 GPU 租金

假设一张 GPU:

$2 / hour

平均吞吐:

1000 tokens/s

那么:

3600 × 1000
=
3.6M tokens/hour

理论 GPU 成本:

$2 / 3.6M
≈ $0.56 / 1M tokens

如果通过优化:

1000 tok/s
↓
2000 tok/s

那么:

$0.56
↓
$0.28 / 1M tokens

GPU 数量没有减少,但单位 Token 成本下降了约 50%。


这就是推理优化真正的经济价值。


因此:

吞吐量提升本质上就是单位 Token 成本下降。


二十四、一个完整的成本优化路线

如果现在有一个成本较高的 LLM 服务,不建议一上来就换模型。


可以按照下面顺序处理。

第一阶段
业务 Token 优化
    │
    ├── Prompt Compression
    ├── RAG TopK
    ├── Output Limit
    └── Conversation Trimming

第二阶段
推理框架优化
    │
    ├── vLLM
    ├── Continuous Batching
    ├── PagedAttention
    └── Chunked Prefill

第三阶段
KV Cache 优化
    │
    ├── Prefix Caching
    ├── GQA / MQA
    └── KV Cache Quantization

第四阶段
模型量化
    │
    ├── FP8
    ├── INT8
    ├── AWQ
    └── GPTQ

第五阶段
Kernel / GPU 优化
    │
    ├── FlashAttention
    ├── CUDA Graph
    ├── Tensor Parallel
    └── Speculative Decoding

这个顺序非常重要。


因为如果:

Prompt 本来就有 20K tokens

你却首先花大量时间优化 CUDA Kernel,收益可能远低于:

20K
↓
8K

这样的上下文优化。


二十五、50%以上成本下降应该如何实现

“成本降低 50%”不能作为一个无条件承诺。


具体收益高度依赖:

模型
GPU
上下文长度
并发
输入/输出 Token 比例
量化方案
Prefix Cache 命中率
业务请求分布

但是在合适的生产场景中,实现 50% 甚至更高的单位 Token 成本下降并非不现实。


一个典型的优化过程可能是:

Baseline

FP16

+
低并发

+
无 Prefix Cache

+
静态 Batch

+
冗余 Context

        ↓

Step 1
Prompt Compression

成本 ↓ 20%

        ↓

Step 2
vLLM + Continuous Batching

吞吐 ↑ 50%

        ↓

Step 3
Prefix Cache

Prefill ↓

        ↓

Step 4
FP8 / INT4

显存 ↓
吞吐 ↑

        ↓

Step 5
KV Cache Optimization

并发 ↑

        ↓

最终

单位 Token 成本
下降 50%+

这里必须强调:


50%并不是某一个参数带来的结果,而是整个推理栈共同优化的结果。


二十六、生产环境推荐的监控指标

推理服务必须建立完整的可观测性。


至少应该监控:

Request Metrics

request_count
request_success
request_error
queue_time
TTFT
TPOT
E2E latency
P50/P95/P99

Token Metrics:

input_tokens
output_tokens
total_tokens
tokens/sec

GPU Metrics:

GPU utilization
GPU memory
SM utilization
Tensor Core utilization
Power
Temperature

KV Cache:

KV Cache Usage
KV Cache Hit Rate
Prefix Cache Hit Rate
Block Allocation
Block Reuse
Eviction

最终形成:

业务指标
     │
     ▼
Token 指标
     │
     ▼
Scheduler 指标
     │
     ▼
KV Cache
     │
     ▼
GPU

只有这样才能定位:

到底是业务 Token 太多?
还是调度效率低?
还是 KV Cache 不够?
还是 GPU Kernel 没吃满?


二十七、一个推荐的生产参数基线

对于一个中等规模的 GPU 推理服务,可以从如下思路开始:

vllm serve /models/Qwen \
  --host 0.0.0.0 \
  --port 8000 \
  --dtype bfloat16 \
  --gpu-memory-utilization 0.90 \
  --max-num-seqs 64 \
  --max-num-batched-tokens 16384

然后逐项压测:

max-num-seqs

32
64
128

以及:

max-num-batched-tokens

4096
8192
16384
32768

建立矩阵:

                    Throughput
                       ↑

                ┌─────────────┐
                │             │
                │ Optimal     │
                │ Region      │
                │             │
                └─────────────┘
                       │
                       └────────→ Latency

目标不是寻找:

最大吞吐

而是寻找:

满足 P95/P99 延迟 SLA 前提下的最高有效吞吐。


二十八、不要把显存利用率等同于计算效率

这是生产部署中非常常见的误判。


例如:

GPU Memory = 90%
GPU Utilization = 35%

说明显存占得很多,但 GPU 计算单元没有充分利用。


可能原因包括:

KV Cache 太大
Batch 太小
Memory Bandwidth 瓶颈
Kernel 不匹配
请求长度差异太大
Decode 占比过高

反过来:

GPU Utilization = 95%
GPU Memory = 70%

也不代表一定需要继续增加 Batch。


可能已经存在:

P99 latency
↑

所以必须同时观察:

Memory
Compute
Latency
Throughput


二十九、vLLM 之外:什么时候考虑 TensorRT-LLM

vLLM 并不是唯一选择。


在 NVIDIA GPU 深度优化场景中,TensorRT-LLM 同样非常重要。


尤其是:

FP8
FP4
KV Cache Quantization
Paged KV Cache
CUDA Kernel
NVIDIA Tensor Core

等场景。


TensorRT-LLM 当前 KV Cache 系统支持 Block 管理、跨请求复用、优先级驱逐以及 MHA/MQA/GQA 等优化;其文档也提供了 KV Cache 容量和复用相关接口。([NVIDIA GitHub][2])


因此实际选型可以考虑:

通用 LLM Serving
        ↓
      vLLM

NVIDIA 深度定制
        ↓
 TensorRT-LLM

PyTorch 原生研究
        ↓
     PyTorch

高性能推理
        ↓
vLLM / TRT-LLM Benchmark

最终仍然应该以真实模型和真实业务负载测试结果为准。


三十、一个可落地的优化决策树

面对一个已经上线的 LLM 服务,可以直接按照下面的顺序排查:

GPU 成本高
   │
   ▼
Token 是否过多?
   │
   ├── 是 → Prompt/RAG/Output 优化
   │
   └── 否
        │
        ▼
GPU 是否吃满?
        │
        ├── 否 → Batch / Scheduler / Kernel
        │
        └── 是
             │
             ▼
KV Cache 是否成为瓶颈?
             │
             ├── 是 → PagedAttention / Prefix Cache /
             │         KV Quantization / GQA
             │
             └── 否
                  │
                  ▼
             模型量化
                  │
                  ▼
             FP8 / INT8 / INT4
                  │
                  ▼
             Speculative Decoding
                  │
                  ▼
             多 GPU 并行

这比“换一张更贵的 GPU”通常更值得先做。


三十一、最终的生产实践原则

大模型推理优化最终可以归纳成八条原则。

第一,先减少 Token,再优化 GPU

少生成 1 Token

永远比:

花更多钱去生成 1 Token

更直接。


第二,KV Cache 是长上下文服务的核心资源

尤其是:

Long Context

+
High Concurrency

场景。


需要重点关注:

KV Memory
Block Allocation
Cache Hit
Eviction


第三,PagedAttention 解决的是内存管理问题

它不是一个简单的 Attention Kernel。


真正价值在于:

KV Cache
    ↓
Block
    ↓
Dynamic Allocation
    ↓
Reuse


第四,Continuous Batching 决定 GPU 是否高效

GPU 最怕:

Batch 太小
请求到达不均匀
长短请求混杂

Continuous Batching 的核心目标就是让 GPU 尽可能持续工作。


第五,Prefix Cache 是 Agent/RAG 的重要优化点

如果:

System Prompt

+
Tools

+
Rules

高度重复,那么 Prefix Cache 往往值得优先测试。


第六,量化必须看真实吞吐

不要简单认为:

INT4 > INT8 > FP8 > FP16

真实结果取决于:

GPU
Kernel
Model
Batch
Context


第七,优化目标不是最大吞吐

真正的目标是:

SLA

+
吞吐

+
成本

+
质量

四者之间取得平衡。


第八,Benchmark 必须使用真实流量模型

最终应该使用:

真实 Prompt 长度
真实输出长度
真实并发
真实 RAG
真实 Agent
真实 Cache Hit Rate

而不是一个简单的:

Hello World

测试。


三十二、总结

vLLM 的价值并不只是“让模型跑得更快”。


它真正解决的是 LLM 推理服务中几个最昂贵的问题:

KV Cache 管理
       +
Continuous Batching
       +
PagedAttention
       +
Prefix Caching
       +
Chunked Prefill
       +
量化
       +
高性能 Kernel

其中,PagedAttention 解决 KV Cache 的碎片和动态分配问题;Continuous Batching 提高 GPU 的持续利用率;Prefix Caching 减少重复 Prompt 的 Prefill 计算;量化降低模型权重和部分运行时数据的存储压力;KV Cache 量化则进一步提高长上下文场景下的并发能力。


从成本角度看,最值得建立的认知是:

LLM 推理成本
        │
        ├── Input Token
        ├── Output Token
        ├── GPU 时间
        ├── KV Cache
        └── 系统利用率

因此,真正成熟的推理优化不是单点优化,而是:

业务层
 ↓
Token 优化
 ↓
模型层
 ↓
量化
 ↓
推理框架
 ↓
vLLM
 ↓
KV Cache
 ↓
Batch Scheduler
 ↓
Kernel
 ↓
GPU

如果一个生产系统能够同时做到:

减少无效 Context

+
提高 Prefix Cache 命中率

+
PagedAttention

+
Continuous Batching

+
合理量化

+
KV Cache 优化

+
真实负载调度

那么在相同硬件下获得显著的吞吐提升、降低单位 Token 成本是完全有可能的。


但“成本降低 50%”不应该被理解成某个 vLLM 参数能够直接带来的固定收益。它取决于具体模型、GPU、上下文长度、并发结构和业务流量。真正可靠的做法,是建立 Baseline,然后逐项 Benchmark,最终以 $/1M tokens、P95 TTFT、TPOT、GPU 利用率和质量指标 来衡量优化是否真正产生了商业价值。


对于 RAG、企业知识库和 AI Agent 来说,尤其应该把 Prompt 设计、Prefix Cache、KV Cache 和 Continuous Batching 放在同一个优化体系里考虑。只有把“模型怎么计算”和“业务到底让模型计算了多少东西”放在一起,推理成本优化才真正进入工程阶段。

参考资料

  1. vLLM 官方文档 —— vLLM 官方技术文档,涵盖 PagedAttention、Continuous Batching、Prefix Caching、量化、Speculative Decoding 等推理能力。([vLLM][1])

  2. Efficient Memory Management for Large Language Model Serving with PagedAttention —— vLLM PagedAttention 核心技术论文,系统讨论 LLM Serving 中 KV Cache 的内存管理问题。

  3. NVIDIA TensorRT-LLM KV Cache System —— NVIDIA 官方 TensorRT-LLM KV Cache 技术文档,介绍 Paged KV Cache、Cache Reuse、MHA/MQA/GQA 等机制。([NVIDIA GitHub][2])

[1]: https://docs.vllm.ai/en/stable/?utm_source=chatgpt.com "vLLM"

[2]: https://nvidia.github.io/TensorRT-LLM/features/kvcache.html?utm_source=chatgpt.com "KV Cache System — TensorRT LLM"

[3]: https://nvidia.github.io/TensorRT-LLM/performance/performance-tuning-guide/fp8-quantization.html?utm_source=chatgpt.com "FP8 Quantization — TensorRT-LLM"