作者: Feiyue Zhai
日期: 2026-04-17
标签: LLM serving, agent, Pareto frontier, prefix cache, queuing theory
交互工具: Agent Serving Pareto Explorer
LLM serving 系统有两个核心指标:
这两个指标天然矛盾:通过增大 batch size 可以提高吞吐量,但每个请求的延迟也会增加。Pareto 曲线(帕累托前沿)描绘了在给定硬件上,throughput 和 latency 之间的最优权衡边界——即不可能在不牺牲一个指标的前提下改善另一个指标的点集。
NVIDIA 的 AIConfigurator 论文提出了一套系统性的方法来生成 LLM serving 的 Pareto 曲线:
AIConfigurator 的核心贡献是将配置搜索从数天的 GPU 实验缩减到 <1 秒的 CPU 计算(427,000× 加速),其 Pareto 分析直观展示了 disaggregated serving 可以比 aggregated 高 53% throughput(Qwen3-235B on 64×H200)。
AIConfigurator 的 Pareto 分析基于标准 serving 假设:每个请求是独立的、单轮的、ISL/OSL 固定的。但现代 AI Agent 系统(如 Cursor、Claude Code、SWE-bench agents)的工作负载有本质不同:
| 维度 | 标准 Serving | Agent Serving |
|---|---|---|
| 请求模式 | 单轮 request-response | 多轮对话,轮数不确定(8-64+ 轮) |
| KV-cache | 每次请求独立 | Prefix cache 跨轮复用,命中率影响巨大 |
| 计算特征 | prefill 一次 + decode 一次 | 每轮都有 incremental prefill + decode |
| Context 增长 | 固定 ISL/OSL | context 随轮次单调增长(累积工具结果) |
| 延迟定义 | TTFT + TPOT | 端到端 task completion time |
| 吞吐定义 | tokens/s | tasks completed/s 或 useful tokens/s |
| 初始 Prompt | 较短(百~千 tokens) | 很长(Cursor system prompt ~16K tokens) |
| 轮间行为 | 无 | 工具执行暂停(0.5s - 30s) |
核心问题:标准 serving 的 Pareto 曲线如何映射到 Agent 场景?Prefix cache、多轮暂停、context 增长如何重塑 Pareto 前沿?
┌──────────────────────────────────┐
│ LLM Serving System │
Session 1 ───→│ (GPU, KV-cache, scheduler) │
Session 2 ───→│ │
... │ Standard metrics: │
Session C ───→│ Throughput X, Latency R │
└──────────────────────────────────┘
C 个并发 Agent sessions,每个 session 的生命周期:
Session i: [Turn 1] ──Z₁──→ [Turn 2] ──Z₂──→ ... ──→ [Turn Nᵢ] → done
每一轮 Agent 与工具交互都会累积新的 context:
Turn j 的 context 长度:
L(j) = L₀ + Σₖ₌₁ʲ⁻¹ [OSL(k) + ΔL_tool(k)] + ΔL_tool(j)
\_/ \________________________________/ \________/
初始 前面所有轮的累积 output + 工具结果 本轮新增
prompt
(16K+)
典型值(代码 Agent,初始 prompt 16K):
| Turn | 累积 context | 本轮新增 ΔL | 本轮输出 OSL |
|---|---|---|---|
| 1 | 16,000 | 16,000 | 128 |
| 5 | 24,512 | 2,128 | 128 |
| 10 | 35,152 | 2,128 | 128 |
| 15 | 45,792 | 2,128 | 128 |
| 20 | 56,432 | 2,128 | 128 |
每轮 agent 输出约 128 tokens(function call + 简短推理),工具返回约 2000 tokens(文件内容/搜索结果)。
Without prefix cache: prefill 整个 L(j) ──→ O(L²) attention
With prefix cache: prefill 仅 ΔL(j) ──→ O(ΔL × L) cross-attention
↑ 小得多
Prefix cache 让每轮的 TTFT 从处理 完整 context(可能 50K+ tokens)降低到只处理增量 tokens(~2K tokens),这是 Agent serving 特有的关键优化。
Agent 系统天然是一个封闭排队系统(closed-loop / think-time model)。每个 session 不断循环:
┌─→ [等待排队] → [Prefill] → [Decode] → [返回结果] ─┐
│ 响应时间 R(j) │
│ ↓
└──────────── [工具执行 / 暂停 Z(j)] ←────────────────┘
由 Little's Law 的封闭系统形式(Operational Analysis):
X = C / (R + Z)
其中:
反解得到 Pareto 关系的基本形式:
R = C / X - Z
这就是 Agent Pareto 曲线的方程。R vs X 的关系被 C 和 Z 参数化。
固定 C = 32 个并发 session,不同的平均暂停时间:
Turn Throughput X (turns/s) ↑
│
Z=0 (无暂停, │ ╱
理论上界) │╱ ← 标准 serving 的 Pareto
│
Z=0.5s (快工具, │ ╱
代码执行) │ ╱
│╱
Z=2s (中等工具, │ ╱
文件读写) │ ╱
│╱
Z=10s (慢工具, │ ╱
web browsing) │ ╱
│ ╱
└──────────────────→ Per-Turn Latency R (ms)
关键洞察:暂停时间 Z 右移了 Pareto 曲线,也降低了有效吞吐量上界。
固定 R 时:X = C / (R + Z)
暂停时间长 → 吞吐量天花板急剧下降,但 GPU 有空余容量可以塞更多 session。
定义活跃比 ρ:
ρ = R / (R + Z)
即一个 session 有多少比例时间在消耗 GPU 计算。
Session 1: [██████]────────[██████]────────[██████]────────
Session 2: ──[██████]────────[██████]────────[██████]──────
Session 3: ────[██████]────────[██████]────────[██████]────
Session 4: ──────[██████]────────[██████]────────[██████]──
↑
GPU sees continuous work if C > 1/ρ
(█ = GPU active, ─ = paused/tool exec)
有效 GPU 并发 = C × ρ = C × R / (R + Z)
为了保持 GPU 满载(有效并发 = B_max):
C_needed = B_max / ρ = B_max × (1 + Z / R)
| Z (暂停) | R (响应) | ρ (活跃比) | 需要的 C 来保持 GPU 满载 (B=32) |
|---|---|---|---|
| 0s | 2s | 100% | 32 |
| 0.5s | 2s | 80% | 40 |
| 2s | 2s | 50% | 64 |
| 10s | 2s | 17% | 192 |
每个活跃 session(无论在计算还是暂停中)都需要在 GPU 上保留 KV-cache。
KV-cache per session:
M_kv(j) = 2 × n_layers × n_kv_heads × d_head × L(j) × sizeof(dtype)
对 70B 模型(80 layers, 8 KV heads via GQA, d=128, FP16):
KV per token = 2 × 8 × 128 × 80 × 2 bytes = 327,680 bytes ≈ 320 KB/token
| Turn | Context L(j) | KV-cache/session | 32 sessions 总占用 |
|---|---|---|---|
| 1 | 16K | 5.1 GB | 163 GB |
| 10 | 35K | 11.2 GB | 358 GB |
| 15 | 46K | 14.7 GB | 470 GB |
| 20 | 56K | 17.9 GB | 573 GB |
8×H100 总显存 = 640 GB,模型权重占 ~140 GB (FP16),可用 KV-cache 约 500 GB。
这意味着:
关键洞察:Agent 的 context 增长导致 KV-cache 挤占内存,可支持的并发 session 数在 task 生命周期内持续下降。这是 Agent serving 独有的动态约束。
影响 1:降低 per-turn latency(R 减小)
Without prefix cache (turn 15, L=46K):
TTFT = prefill(46,000 tokens) ≈ 800ms (at batch=1)
≈ 3,000ms (at batch=32)
With prefix cache (turn 15, ΔL=2,128):
TTFT = incr_prefill(2,128 tokens) ≈ 38ms (at batch=1)
≈ 120ms (at batch=32)
TTFT 差异:800ms → 38ms(21× 提速)。
影响 2:Cache 命中率取决于暂停时间 Z
Cache hit rate h(Z):
h 1.0 ┤████████████████████████████
│ ╲
0.8 ┤ ╲
│ ╲
0.5 ┤ ╲
│ ╲
0.2 ┤ ╲
│ ╲______
0.0 ┤
└──┴──┴──┴──┴──┴──┴──┴──┴──┴──→ Z (暂停时间)
0 1 2 5 10 20 30 60 sec
↑ ↑
cache 安全区 cache 开始被 evict
暂停时间短 → cache 命中率高 → prefill 快 → latency 低
暂停时间长 → cache 被 evict → 全量 re-prefill → latency 高
这形成一个正反馈环:
Z ↑ ⟹ h ↓ ⟹ R ↑ ⟹ ρ ↓ ⟹ C_needed ↑ ⟹ memory pressure ↑ ⟹ h ↓
每一轮的 LLM 推理延迟可以分解为:
R(j) = TTFT(j) + TPOT(B_eff) × OSL
TTFT(Time To First Token):
T_prefill(j) = h(Z) · T_incr(ΔL_j) + (1 - h(Z)) · T_full(L_j)
其中 T_incr 和 T_full 由算子级模型决定:
TTFT(L) = 2 × active_params × L / total_FLOPS / η_prefill
η_prefill ≈ 0.65(prefill 是 compute-bound,大 kernel 摊薄了 launch 开销,效率高于 decode)。
TPOT(Time Per Output Token):
由 decode step time 决定,受 memory bandwidth 限制:
step_time_ideal = max(T_weight_load + T_kv_load, T_compute)
T_weight_load = active_params × dtype_bytes / total_bandwidth [恒定]
T_kv_load = B × L_avg × kv_bytes_per_token / BW [随 batch 和 context 增长]
T_compute = 2 × active_params × B / total_FLOPS [随 batch 增长]
TPOT = step_time_ideal / η_decode
注意 KV-cache 带宽受 GQA head 数约束:
kv_effective_GPUs = min(numGPUs, n_kv_heads)
T_kv_load = B × L_avg × kv_bytes_per_token / (kv_effective_GPUs × BW_per_GPU)
当 numGPUs > n_kv_heads(如 8 KV heads on 16 GPUs),KV-cache 无法进一步拆分,每张卡至少保留 1 个完整 head。此时 T_kv 不再随 GPU 数量缩减。
加上 10% elementwise 开销(LayerNorm, RoPE, activation, residual add):
T_memory = (T_weight_load + T_kv_load) × 1.10
其中 η_decode ≈ 0.40 是 decode 效率因子,补偿 roofline 模型未覆盖的实际开销:
| 开销来源 | 典型值 (70B, TP=8) | 说明 |
|---|---|---|
| TP all-reduce 通信 | ~4-6ms | 每层 2 次 all-reduce × 80 层,每次 ~30μs latency |
| Kernel launch | ~2-3ms | 每层 10+ kernel × 80 层 = 800+ kernel launches |
| 非 matmul 算子 | ~1-2ms | LayerNorm, RoPE, activation, residual add |
| 内存管理 | ~0.5-1ms | Paged attention, cache lookup |
验证:70B FP16, 8×H100, B=1
系统参数:
| 符号 | 单位 | 定义 |
|---|---|---|
| G | 个 | GPU 数量 |
| BW | GB/s | 单卡 HBM 带宽 |
| F | TFLOPS | 单卡算力(按模型精度选 FP16/FP8/FP4) |
| M_gpu | GB | 单卡显存 |
模型参数:
| 符号 | 单位 | 定义 | 示例 (Llama-70B) | 示例 (K2.5) |
|---|---|---|---|---|
| P_total | B | 模型总参数量 | 70 | 1000 |
| P_active | B | 每 token 激活参数量 | 70 | 32 |
| d | bytes | 每参数字节数 | 2 (FP16) | 0.5 (FP4) |
| n_kv | 个 | KV head 数(GQA)或 1(MLA) | 8 | 1 |
| κ | bytes | 每 token KV-cache 大小(所有层) | 327,680 | 70,272 |
| η_d | — | decode 效率因子 | 0.40 | 0.40 |
| η_p | — | prefill 效率因子 | 0.65 | 0.65 |
MoE 专用参数(dense 模型无此项):
| 符号 | 单位 | 定义 | 示例 (K2.5) |
|---|---|---|---|
| E | 个 | routed expert 总数 | 384 |
| K | 个 | 每 token 激活 expert 数 (topK) | 8 |
| P_expert | B | 单个 expert 参数量 | 0.04404 |
| P_nonmoe | B | 非 MoE 参数(attention + shared + embed) | 11.5 |
Agent 工作负载参数:
| 符号 | 单位 | 定义 |
|---|---|---|
| C | 个 | 并发 session 数 |
| N | 轮 | 每 session 轮数 |
| Z | ms | 轮间暂停时间(工具执行) |
| L₀ | tokens | 初始 prompt 长度(system prompt) |
| ΔL | tokens | 每轮新增 tokens(工具返回结果) |
| OSL | tokens | 每轮输出 tokens |
| h | — | prefix cache 命中率 (0~1) |
派生量:
| 符号 | 单位 | 定义 | 公式 |
|---|---|---|---|
| L(j) | tokens | 第 j 轮的 context 长度 | L₀ + (j-1) × (OSL + ΔL) |
| B_eff | 个 | 稳态有效 batch size | C × ρ |
| ρ | — | GPU 活跃比 | R / (R + Z) |
| C_max | 个 | KV 内存限制的最大并发 | ⌊M_avail / M_kv_session⌋ |
方程 1: Per-turn latency R(j)
第 j 轮的端到端推理延迟 = TTFT + decode 时间:
R(j) = TTFT(j) + TPOT(j) × OSL
方程 2: TTFT — prefill 时间(compute-bound)
Prefill 处理输入 tokens,计算量由 active params 和输入长度决定:
L_prefill(j) = h × ΔL + (1 - h) × L(j) # cache 命中时只处理增量
TTFT(j) = 2 × P_active × L_prefill(j) / (G × F × 10¹²) × 10³ / η_p [ms]
方程 3: TPOT — decode step 时间(memory-bound)
每个 decode step 需要加载权重 + KV-cache,产生 B_eff 个 token:
TPOT(j) = max(T_mem(j), T_compute) / η_d [ms]
T_mem(j) = (T_weight + T_kv(j)) × 1.10 # ×1.10 = 10% elementwise 开销
方程 3a: T_weight — 模型权重加载时间
Dense 模型(Llama):权重 TP-split 到 G 张卡:
T_weight = P_active × d / (G × BW) × 10³ [ms]
MoE 模型(K2.5):拆分为 non-MoE + batch-dependent expert loading:
T_nonmoe = P_nonmoe × d / (G × BW) × 10³ [ms] # TP-split
E_active = min(B_eff × K, E) # 活跃 expert 数(batch-dependent)
E_per_gpu = E_active / G # 每卡加载的 expert 数
T_moe = E_per_gpu × P_expert × d / BW × 10³ [ms] # 每卡串行加载
T_weight = T_nonmoe + T_moe
关键:小 batch 时 E_active ≪ E(如 B=1, K=8 → 只加载 8/384 = 2% expert),weight loading 很快。大 batch 时所有 expert 都活跃。
方程 3b: T_kv — KV-cache 加载时间
每个 decode step 读取 batch 中所有 request 的全部 KV-cache 用于 attention:
G_kv = min(G, n_kv) # KV 有效 GPU 数
T_kv(j) = B_eff × L(j) × κ / (G_kv × BW × 10⁹) × 10³ [ms]
方程 3c: T_compute — 计算时间
T_compute = 2 × P_active × B_eff / (G × F × 10¹²) × 10³ [ms]
Decode 通常 memory-bound(T_mem ≫ T_compute),但超大 batch 时可能翻转为 compute-bound。
方程 4: 封闭排队稳态(Little's Law)
Agent session 循环:推理 R → 暂停 Z → 推理 R → ...
ρ = R / (R + Z) # GPU 活跃比
B_eff = C_eff × ρ # 稳态有效 batch
X_turn = C_eff / (R/10³ + Z/10³) # turn 吞吐 [turns/s]
B_eff 与 R 互相依赖(R 取决于 B_eff 通过 TPOT,B_eff 取决于 R 通过 ρ),需迭代求解:
初始: B_eff = 1
重复:
TPOT = computeStepTime(B_eff, L(j))
R = TTFT + TPOT × OSL
ρ = R / (R + Z)
B_eff_new = C_eff × ρ
若 |B_eff_new - B_eff| < ε 则收敛
B_eff ← 0.6 × B_eff + 0.4 × B_eff_new # damped update
方程 5: KV-cache 内存约束
KV-cache 内存限制最大并发 session 数:
M_model = P_total × d # 模型权重显存 [GB]
M_avail = G × M_gpu - M_model # 可用于 KV 的总显存 [GB]
r_kv = G / min(G, n_kv) # KV 复制因子(GPU > kvHeads 时 >1)
M_kv_session = L̄ × κ × r_kv / 10⁹ # 每 session KV 占用 [GB]
C_max = ⌊M_avail / M_kv_session⌋
C_eff = min(C, C_max)
L̄ 使用 session 平均 context 长度。当 G > n_kv 时,KV 需复制到多卡,r_kv > 1,C_max 降低。
方程 6: Task-level 映射
从 turn-level 聚合到 task-level:
T_task = Σ_{j=1}^{N} [R(j)/10³ + Z/10³] [s]
X_task = X_turn / N [tasks/s]
方程 7: 标准 Serving → Agent Serving 映射
已有标准 Pareto 数据时的快速估算:
X_agent ≈ X_serving(L_prefill_eff, OSL, B_eff) / N × ρ
| 因子 | 说明 |
|---|---|
| / N | 每个 task 包含 N 轮 |
| × ρ | GPU 只有 ρ 比例时间在工作(其余在等工具) |
| L_prefill_eff | 有效 prefill 长度(取决于 cache 命中率和轮次) |
硬件: 70B 模型, 8×H100
| 参数 | Code Agent | Research Agent | Deep Reasoning |
|---|---|---|---|
| 并发 C | 32 | 16 | 8 |
| 平均轮数 N | 8 | 15 | 20 |
| 暂停 Z | 0.5s (代码执行) | 5.0s (web 搜索) | 1.0s (思考链) |
| 初始 prompt L₀ | 16K | 24K | 32K |
| ΔL per turn | 500 tok | 3000 tok | 4000 tok |
| OSL per turn | 128 tok | 64 tok | 4096 tok |
| 最终 context | ~21K | ~70K | ~113K |
| Cache 命中率 | ~95% | ~70% | ~85% |
| 活跃比 ρ | ~67% | ~12% | ~82% |
推演结果(含效率因子 η_decode=0.40, η_prefill=0.65):
| 指标 | Code Agent | Research Agent | Deep Reasoning |
|---|---|---|---|
| TTFT (有 cache) | ~55ms | ~200ms | ~175ms |
| TTFT (无 cache) | ~580ms | ~3,800ms | ~6,200ms |
| TPOT | ~13ms | ~14ms | ~18ms |
| Per-turn latency R | ~1.7s | ~1.1s | ~74s |
| GPU 利用率 | ~77% | ~18% | ~99% |
| Max sessions (KV) | ~73 | ~21 | ~13 |
| Token throughput/GPU | ~155 tok/s | ~7.5 tok/s | ~36 tok/s |
关键观察:
从标准 serving 的 Pareto 数据可以直接推导 Agent 场景的性能:
X_agent = X_serving(effective_ISL, OSL, B_eff) / N × 1 / (1 + Z/R)
即:标准 serving 的吞吐量 → 除以平均轮数 N → 再乘以活跃比 ρ = R/(R+Z)。
如果有 AIConfigurator 对某个模型/硬件的标准 Pareto 数据,可以用这个公式将它映射到 Agent 场景下的 Pareto。
Z 越大,曲线右移且下压。但这意味着 GPU 有大量空闲——可以通过增加并发 C 来填满,前提是 KV-cache 内存允许。
标准 Pareto 是 2D(throughput vs latency),但 Agent 场景需要加上第三个约束轴:KV-cache memory / 最大并发 session 数。一个看似很好的 throughput-latency 配置点可能因为 KV-cache 溢出而不可行。
现代 agent 系统的 system prompt 非常长(Cursor ~16K tokens, Claude Code 类似),这意味着:
对于 MoE + MLA 模型(如 Kimi K2.5, 1T total / 32B active, MLA d_c=512):
MLA 的 KV-cache 计算(Kimi K2.5 / DeepSeek-V3 架构):
MLA 不存储 K/V 原始向量,而是存储压缩后的 latent 向量 c_KV + 解耦的 RoPE key k_rope:
KV/token/layer = (d_c + d_rope) × sizeof(dtype)
= (512 + 64) × 2 = 1,152 bytes
KV/token = 1,152 × 61 layers = 70,272 bytes ≈ 69 KB
(注:K2.5 实际 61 层,来自 HuggingFace config.json)
对比标准 GQA(Llama-70B)的 320 KB/token,MLA 压缩了 4.6×。
MLA 对 TP 拆分的影响:c_KV 是整体压缩向量,不按 head 拆分。每张 GPU 需读取完整 c_KV 来重建自己负责的 Q heads 对应的 K/V。因此 MLA 的 kvHeads 等效为 1,KV 带宽不随 TP 缩减——但绝对值小(90 KB vs 320 KB/token)。
MoE + MLA 在 Agent 场景下的优势:计算快(active params 少)+ KV-cache 小(MLA 压缩)→ 可支持更多并发 session,特别适合长 context 的 agent workload。
Agent Serving Pareto Explorer 基于以下性能模型实现:
TTFT(L) = 2 × active_params × L / total_FLOPS / η_prefill (η_prefill ≈ 0.65)
Dense 模型(Llama 系列):
T_weight = model_params × dtype_bytes / (numGPUs × BW_per_GPU) # TP-split
T_kv = B × L_avg × kvBPT / (min(numGPUs, kvHeads) × BW) # GQA head-split
T_compute = 2 × model_params × B / total_FLOPS
T_memory = (T_weight + T_kv) × 1.10 # +10% elementwise
TPOT = max(T_memory, T_compute) / η_decode
MoE 模型(Kimi K2.5)—— 权重加载拆分为 non-MoE + MoE:
# non-MoE: attention + shared experts + embeddings, TP-split
T_nonmoe = nonMoeB × dtype_bytes / (numGPUs × BW_per_GPU)
# MoE: only active experts load weights, distributed across GPUs
active_experts = min(B × topK, totalExperts) # e.g. min(10×8, 384) = 80
active_per_gpu = active_experts / numGPUs # e.g. 80/8 = 10
T_moe = active_per_gpu × expertB × dtype_bytes / BW_per_GPU
T_weight = T_nonmoe + T_moe
T_kv = B × L_avg × kvBPT / (min(numGPUs, kvHeads) × BW)
T_compute = 2 × activeSize × B / total_FLOPS
T_memory = (T_weight + T_kv) × 1.10
TPOT = max(T_memory, T_compute) / η_decode
K2.5 的 expert 极小(44M = 22MB in FP4),weight loading 瓶颈在 non-MoE(11.5B)和 KV-cache,不在 expert。小 batch 时大量 expert 不被路由,进一步减少 weight loading。
模型参数来源: 所有配置从 HuggingFace config.json 获取:
对于给定的并发 C,通过迭代求解稳态 B_eff:
repeat:
tpot = computeStepTime(B_eff, L_avg, hw)
R = TTFT + tpot × OSL
ρ = R / (R + Z)
B_eff_new = C × ρ
until converged
| GPU | HBM BW | FP16 TFLOPS | FP8 TFLOPS | FP4 TFLOPS | VRAM |
|---|---|---|---|---|---|
| H100 SXM | 3,350 GB/s | 989 | 1,979 | — | 80 GB |
| H200 SXM | 4,800 GB/s | 989 | 1,979 | — | 141 GB |
| B200 | 8,000 GB/s | 2,250 | 4,500 | 9,000 | 192 GB |
| MI355X | 8,000 GB/s | 2,500 | 5,000 | 10,000 | 288 GB |
| 日期 | 变更 |
|---|---|
| 2026-04-17 | 初始版本:完整推演 + 交互工具 |