文章摘要
2026 年,LLM 推理优化已经从单一的模型量化发展为涵盖架构设计、调度策略、内存管理、硬件协同的全栈工程。本文系统梳理 LLM 推理优化的完整技术图谱:Prefill-Decode 分离架构(PD Separation)、投机解码(Speculative Decoding)、PagedAttention v2、动态批处理、KV Cache 压缩、以及 vLLM/TensorRT-LLM/SGLang 三大推理引擎的深度对比与选型指南。
1LLM 推理的性能瓶颈:为什么推理比训练更难优化?
LLM 推理的核心矛盾是「内存带宽瓶颈」。与训练阶段不同,推理阶段每次生成一个 token 时,需要将整个模型权重从显存加载到计算单元——但只执行一次矩阵-向量乘法。这意味着计算单元大部分时间在等待数据搬运,而非执行计算。
用一个直观的比喻:训练像是在工厂里批量生产——原材料(数据)和生产线(GPU 计算单元)都在工厂里,生产效率很高。推理像是快递配送——每次只送一个包裹(一个 token),但快递员必须跑遍整个仓库(遍历所有模型权重)才能找到那个包裹。
LLM 推理的两个阶段有不同的瓶颈:
Prefill 阶段(预填充):处理用户输入的 prompt,是一次大规模的矩阵-矩阵乘法(GEMM)。这个阶段的瓶颈是计算吞吐量——需要快速处理数千到数万 token 的输入。
Decode 阶段(解码):逐个生成输出 token,每次只处理一个 token。这个阶段的瓶颈是内存带宽——每个 token 生成都需要读取全部模型权重,但计算量极小。
这种两阶段的不同特性催生了 2026 年最重要的推理优化架构——Prefill-Decode 分离(PD Separation)。
关键指标:
💡 一句话理解
理解 LLM 推理优化的第一步:区分 Prefill 和 Decode 阶段的不同瓶颈。Prefill 是计算密集型(compute-bound),Decode 是内存带宽密集型(memory-bound)。不同的优化技术针对不同的阶段。
2Prefill-Decode 分离架构:2026 年推理部署的标准范式
PD 分离(Prefill-Decode Separation)是 2026 年 LLM 推理部署最重要的架构创新。其核心思想是:将 Prefill 阶段和 Decode 阶段部署在不同的 GPU 上,因为两个阶段对硬件的需求完全不同。
Prefill 阶段需要高计算吞吐量。处理一个 4K token 的 prompt 需要数百 TFLOPs 的计算量。最适合的硬件是高算力 GPU(如 NVIDIA H200、B200),这些 GPU 的 FP8 算力可达数千 TFLOPs。
Decode 阶段需要高内存带宽。每生成一个 token 需要读取整个模型权重。对于 70B 参数的模型(INT4 量化后约 35GB),每 token 需要读取 35GB 数据。最适合的硬件是高内存带宽 GPU(如 NVIDIA H200 的 4.8TB/s HBM3e 带宽)。
分离架构的工作流程:
- 用户请求到达 Prefill 节点
- Prefill 节点处理完整 prompt,生成 KV Cache
- KV Cache 通过高速互联(NVLink/NVSwitch)传输到 Decode 节点
- Decode 节点逐个生成输出 token
- 生成的 token 流式返回给用户
性能提升:在典型的 70B 模型部署中,PD 分离架构相比混合部署可以提升 2-3 倍的吞吐量,同时将 TTFT 降低 40-60%。这是因为 Prefill 和 Decode 不再争抢同一块 GPU 的计算和内存资源。
Disaggregated Serving 的进阶形态。2026 年的最新进展是完全解耦的推理服务——Prefill 池和 Decode 池可以独立扩缩容,甚至可以使用不同类型的 GPU。例如:Prefill 用 H100(高算力),Decode 用 L40S(高内存带宽、低成本)。
"""
Prefill-Decode 分离架构部署配置
使用 vLLM 0.8 的 PD 分离模式
"""
from vllm import LLM, SamplingParams
from vllm.distributed import PDSeparationConfig
# ── 1. Prefill 节点配置 ──
prefill_config = {
"model": "meta-llama/Llama-4-70B-Instruct",
"tensor_parallel_size": 4, # 4 卡张量并行
"gpu_memory_utilization": 0.95,
"max_model_len": 32768,
"dtype": "bfloat16",
"kv_transfer_config": {
"role": "prefill", # Prefill 角色
"connector": "nccl", # 使用 NCCL 传输 KV Cache
"target_nodes": ["decode-0", "decode-1", "decode-2"],
},
# Prefill 专用优化
"enable_chunked_prefill": True, # 分块 Prefill(减少内存峰值)
"chunk_size": 2048, # 每块 2048 tokens
"scheduler": "prefill_optimized", # Prefill 优化的调度策略
}
# ── 2. Decode 节点配置 ──
decode_config = {
"model": "meta-llama/Llama-4-70B-Instruct",
"tensor_parallel_size": 2, # Decode 只需 2 卡
"gpu_memory_utilization": 0.90,
"max_model_len": 32768,
"dtype": "bfloat16",
"kv_transfer_config": {
"role": "decode", # Decode 角色
"connector": "nccl",
"source_nodes": ["prefill-0"],
},
# Decode 专用优化
"speculative_config": {
"model": "meta-llama/Llama-4-8B-Instruct", # 8B 草稿模型
"num_speculative_tokens": 6, # 每次推测 6 个 token
"acceptance_threshold": 0.7, # 接受率阈值
},
"scheduler": "decode_optimized", # Decode 优化的调度策略
"max_batch_size": 256, # 更大的批处理大小
}
# ── 3. 启动 PD 分离服务 ──
def start_pd_separation_service():
"""启动 PD 分离推理服务"""
# 启动 Prefill 节点
prefill_engine = LLM(
**prefill_config,
worker_cls="vllm.worker.PrefillWorker",
)
# 启动 Decode 节点(可多实例)
decode_engines = []
for i in range(3):
engine = LLM(
**decode_config,
worker_cls="vllm.worker.DecodeWorker",
)
decode_engines.append(engine)
# 启动负载均衡器
from vllm.serving import PDRouter
router = PDRouter(
prefill_engines=[prefill_engine],
decode_engines=decode_engines,
routing_strategy="min_decode_queue", # 路由到队列最短的 Decode 节点
health_check_interval=5,
)
# 启动 API 服务
router.serve(
host="0.0.0.0",
port=8000,
api_type="openai", # 兼容 OpenAI API
)
# ── 4. 性能监控 ──
class PDMetrics:
"""PD 分离架构的性能指标"""
def __init__(self):
self.metrics = {
"prefill_latency_ms": [], # Prefill 延迟
"decode_latency_per_token": [], # 每 token 解码延迟
"ttft_ms": [], # 首 token 延迟
"throughput_tokens_per_sec": [], # 吞吐量
"kv_transfer_ms": [], # KV Cache 传输延迟
"spec_acceptance_rate": [], # 投机解码接受率
}
def report(self):
"""生成性能报告"""
import numpy as np
report = {}
for key, values in self.metrics.items():
if values:
report[key] = {
"mean": np.mean(values),
"p50": np.percentile(values, 50),
"p99": np.percentile(values, 99),
}
print("═══ PD 分离架构性能报告 ═══")
for metric, stats in report.items():
print(f"{metric}:")
print(f" 均值: {stats['mean']:.2f}")
print(f" P50: {stats['p50']:.2f}")
print(f" P99: {stats['p99']:.2f}")
return report💡 一句话理解
PD 分离架构的最低部署规模是 4 块 GPU(2 块 Prefill + 2 块 Decode)。如果你的 GPU 数量少于 4 块,PD 分离的收益不大,建议先用传统的混合部署模式。
3投机解码(Speculative Decoding):用 8B 模型加速 70B 模型
投机解码是 2026 年最实用的推理加速技术。其核心思想极其巧妙:用一个小的「草稿模型」(Draft Model)快速生成多个候选 token,然后用大的「目标模型」(Target Model)一次性验证这些 token 是否正确。
为什么这能加速? 因为大模型验证 N 个 token 的时间几乎等于生成 1 个 token 的时间——两者都需要读取完整的模型权重。如果草稿模型的预测有 70% 的接受率,那么每次验证 6 个 token,平均可以接受 4.2 个,相当于将 Decode 速度提升了 4 倍。
投机解码的数学原理:
假设草稿模型生成了 token 序列 [t₁, t₂, t₃, t₄, t₅, t₆],目标模型需要验证每个 token。验证方法是:对于第 i 个位置,比较草稿模型的概率分布 q(t|x₁...xᵢ₋₁) 和目标模型的概率分布 p(t|x₁...xᵢ₋₁)。
接受-拒绝采样规则:
关键洞察:这个采样方案保证了输出分布与不使用投机解码时完全相同——投机解码是一种无损加速技术。
2026 年的投机解码变体:
4PagedAttention v2 与 KV Cache 内存管理
KV Cache 是 LLM 推理中最被低估的内存消耗者。对于一个 70B 参数的模型,在 32K 上下文长度下,单个请求的 KV Cache 可能占用 2-4 GB 显存——几乎与模型权重本身相当。如果有 100 个并发请求,KV Cache 将占用 200-400 GB 显存。
PagedAttention(2023 年 vLLM 首次提出)的核心思想是将 KV Cache 分块管理,类似于操作系统的虚拟内存分页。2026 年的 PagedAttention v2 在三个方面有了重大升级:
动态分页粒度。v1 使用固定的页大小(如 16 token),v2 根据注意力头的实际访问模式动态调整页大小。对于长上下文(>16K token),使用更大的页(64 token)减少页表开销;对于短上下文,使用小页(8 token)减少内存浪费。
跨请求 KV Cache 共享。如果多个请求共享相同的 system prompt(这在生产环境中非常常见),PagedAttention v2 可以让它们共享同一份 KV Cache 页,而不是为每个请求重复计算和存储。这在多轮对话场景中可以将 KV Cache 的内存占用降低 50-80%。
KV Cache 压缩。v2 支持将 KV Cache 从 FP16 压缩到 INT4/INT8,在可接受的精度损失下(<1% 的 perplexity 增加)将 KV Cache 的内存占用减少 4-8 倍。压缩算法使用 per-channel 量化,对 Key 和 Value 使用不同的量化策略(Key 对精度更敏感,使用 INT8;Value 可以使用 INT4)。
内存预算管理。PagedAttention v2 引入了KV Cache 内存预算的概念——为每个请求分配固定的 KV Cache 预算,当预算耗尽时,使用「注意力稀疏化」策略只保留最重要的注意力头/位置,而非简单地截断上下文。
"""
KV Cache 优化:PagedAttention v2 配置
"""
from vllm import LLM, SamplingParams
# ── 1. PagedAttention v2 配置 ──
llm = LLM(
model="meta-llama/Llama-4-70B-Instruct",
tensor_parallel_size=4,
# PagedAttention v2 配置
block_manager_config={
"version": "v2", # 使用 v2 分页管理
"block_size": "dynamic", # 动态分页粒度
"min_block_size": 8, # 最小页大小
"max_block_size": 64, # 最大页大小
"enable_prefix_sharing": True, # 跨请求前缀共享
"prefix_sharing_strategy": "radix", # 基于 Radix Tree 的共享
},
# KV Cache 压缩
kv_cache_config={
"dtype": "auto", # 原始精度(跟随模型)
"compression": {
"enabled": True,
"key_dtype": "int8", # Key 用 INT8
"value_dtype": "int4", # Value 用 INT4
"calibration_method": "minmax", # 量化校准方法
"group_size": 128, # 分组量化大小
},
# 内存预算
"memory_budget_gb": 40, # KV Cache 总预算 40GB
"per_request_budget_gb": 2, # 每个请求预算 2GB
"eviction_policy": "lru", # 淘汰策略
},
# 注意力稀疏化(当 KV Cache 预算耗尽时)
attention_config={
"sparse_attention": {
"enabled": True,
"strategy": "topk_heads", # 保留最重要的注意力头
"keep_ratio": 0.7, # 保留 70% 的头
"trigger_threshold": 0.9, # 内存使用 >90% 时触发
},
},
gpu_memory_utilization=0.95,
max_model_len=32768,
)
# ── 2. 前缀共享效果演示 ──
def demo_prefix_sharing():
"""演示跨请求前缀共享的内存节省"""
# 共享的 System Prompt(约 2000 tokens)
system_prompt = "你是一个专业的 AI 助手..."
# 多个用户请求(共享同一个 system prompt)
requests = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": "解释量子计算"},
]
requests_2 = [
{"role": "system", "content": system_prompt}, # 相同!
{"role": "user", "content": "解释相对论"},
]
# 不使用前缀共享:每个请求独立计算 system prompt 的 KV Cache
# 内存占用:2 × KV(system_prompt) + KV(user_1) + KV(user_2)
# 使用前缀共享:system prompt 的 KV Cache 只计算一次
# 内存占用:1 × KV(system_prompt) + KV(user_1) + KV(user_2)
# 节省:约 2000 tokens 的 KV Cache
outputs = llm.chat(requests, SamplingParams(temperature=0.7))
outputs_2 = llm.chat(requests_2, SamplingParams(temperature=0.7))
# 查看内存使用情况
cache_stats = llm.get_kv_cache_stats()
print(f"总页数: {cache_stats['total_pages']}")
print(f"共享页数: {cache_stats['shared_pages']}")
print(f"共享率: {cache_stats['shared_pages'] / cache_stats['total_pages']:.1%}")
print(f"节省内存: {cache_stats['saved_memory_gb']:.1f} GB")
# ── 3. KV Cache 压缩精度测试 ──
def test_kv_compression_accuracy():
"""测试 KV Cache 压缩对输出质量的影响"""
test_prompts = [
"解释 Transformer 的自注意力机制",
"写一首关于春天的诗",
"分析 2026 年 AI 芯片市场趋势",
]
# 无压缩基线
llm_fp16 = LLM(
model="meta-llama/Llama-4-70B-Instruct",
kv_cache_config={"compression": {"enabled": False}},
)
# INT8 Key + INT4 Value 压缩
llm_compressed = LLM(
model="meta-llama/Llama-4-70B-Instruct",
kv_cache_config={"compression": {
"enabled": True,
"key_dtype": "int8",
"value_dtype": "int4",
}},
)
for prompt in test_prompts:
out_fp16 = llm_fp16.generate(prompt)
out_comp = llm_compressed.generate(prompt)
# 计算输出相似度
similarity = compute_semantic_similarity(out_fp16, out_comp)
print(f"Prompt: {prompt[:30]}...")
print(f" 语义相似度: {similarity:.3f}")
print(f" 内存节省: {get_memory_savings():.1f}x")💡 一句话理解
前缀共享(Prefix Sharing)在多轮对话和共享 System Prompt 的场景中效果极佳。如果你的应用有固定的 System Prompt(如角色扮演、专业助手),务必开启前缀共享——它可以节省 50-80% 的 KV Cache 内存。
⚠️ 常见踩坑
KV Cache 压缩(INT4/INT8)会引入轻微的精度损失。对于大多数应用(聊天、摘要、代码生成),这种损失可以忽略(<1% perplexity 增加)。但对于需要精确数值推理的任务(如数学计算、数据分析),建议保持 FP16 KV Cache。
5推理引擎选型:vLLM vs TensorRT-LLM vs SGLang
2026 年的三大 LLM 推理引擎各有明确的定位和优势。选择正确的引擎可以将推理成本降低 2-5 倍。
vLLM 0.8:最通用的选择。vLLM 的优势在于模型兼容性最广(支持几乎所有开源模型)、社区最活跃、更新最快。0.8 版本引入了原生 PD 分离支持和 PagedAttention v2。适合大多数团队作为默认选择。
TensorRT-LLM 0.12:极致性能。NVIDIA 的 TensorRT-LLM 在 NVIDIA GPU 上可以实现最高的推理吞吐量,但代价是:只支持 NVIDIA GPU、模型支持范围较窄(主要是 Llama、Mistral、Qwen 等主流架构)、配置复杂。适合对性能有极致要求且使用 NVIDIA GPU 的场景。
SGLang 0.6:结构化生成的最佳选择。SGLang 的核心优势是结构化输出(JSON Schema、正则表达式约束)的性能——如果你的应用需要 LLM 输出严格符合 JSON Schema 的结果(如函数调用、数据提取),SGLang 比 vLLM 快 5-10 倍。
选型决策矩阵:
- 通用部署、快速上手 → vLLM
- 极致性能、NVIDIA GPU → TensorRT-LLM
- 结构化输出、函数调用 → SGLang
- 边缘部署、低资源 → llama.cpp / MLC-LLM
- 云端大规模 → vLLM + PD 分离
| 特性 | vLLM 0.8 | TensorRT-LLM 0.12 | SGLang 0.6 |
|---|---|---|---|
模型兼容性 | ⭐⭐⭐⭐⭐ 几乎所有 | ⭐⭐⭐ 主流架构 | ⭐⭐⭐⭐ 大多数 |
推理吞吐量 | ⭐⭐⭐⭐ 优秀 | ⭐⭐⭐⭐⭐ 极致 | ⭐⭐⭐⭐ 优秀 |
PD 分离 | ✅ 原生支持 | ✅ 支持 | ⚠️ 实验性 |
投机解码 | ✅ 支持 | ✅ 支持 | ✅ 支持 |
结构化输出 | ⭐⭐⭐ 一般 | ⭐⭐⭐ 一般 | ⭐⭐⭐⭐⭐ 极致 |
社区活跃度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
部署复杂度 | ⭐⭐⭐⭐⭐ 简单 | ⭐⭐⭐ 较复杂 | ⭐⭐⭐⭐ 简单 |
边缘部署 | ❌ 不支持 | ❌ 不支持 | ❌ 不支持 |
💡 一句话理解
如果你不确定选哪个,选 vLLM。它是 2026 年的「安全选择」——性能不是最好的,但足够好、兼容性最广、文档最全、社区最活跃。等你有了明确的性能瓶颈再考虑迁移到 TensorRT-LLM 或 SGLang。
⚠️ 常见踩坑
不要在开发阶段就追求极致推理优化。先用 vLLM 的默认配置跑通整个应用流程,确认功能正确后再逐步开启优化(量化、PD 分离、投机解码)。过早优化是工程大忌。
6实战:构建百万级并发的 LLM 推理服务
本节以一个完整的案例演示如何构建支持百万级日活用户的 LLM 推理服务。
架构设计:采用三层架构——API 网关层(负载均衡 + 限流)、推理调度层(请求路由 + 批处理)、推理执行层(PD 分离的 GPU 集群)。
关键设计决策:
- 动态批处理(Dynamic Batching):将多个同时到达的请求合并为一个批次处理,提高 GPU 利用率。关键是设置合理的「最大等待时间」——等太久会增加延迟,等太短会降低批处理效率。
- 请求优先级队列:VIP 用户的请求优先处理,普通用户排队等待。
- 自动扩缩容:根据队列长度和 GPU 利用率自动增减 Decode 节点。
- 请求中断与恢复:用户取消请求时,立即释放其占用的 KV Cache 内存。
"""
生产级 LLM 推理服务架构
支持百万级日活、自动扩缩容、请求优先级
"""
import asyncio
from dataclasses import dataclass
from enum import Enum
from typing import Optional
import time
# ── 1. 请求模型 ──
@dataclass
class InferenceRequest:
request_id: str
prompt: str
max_tokens: int
temperature: float
priority: int # 0=VIP, 1=Normal, 2=Background
created_at: float = time.time()
user_id: Optional[str] = None
@property
def estimated_prefill_tokens(self) -> int:
"""估算 Prefill token 数"""
return len(self.prompt) // 4 # 粗略估算:4 字符 ≈ 1 token
# ── 2. 动态批处理器 ──
class DynamicBatcher:
"""动态批处理器:合并多个请求以提高 GPU 利用率"""
def __init__(
self,
max_batch_size: int = 256,
max_wait_ms: int = 10, # 最大等待时间 10ms
max_total_tokens: int = 128000, # 批次最大 token 数
):
self.max_batch_size = max_batch_size
self.max_wait_ms = max_wait_ms
self.max_total_tokens = max_total_tokens
self.pending_queue: list[InferenceRequest] = []
self.batch_ready_event = asyncio.Event()
async def submit(self, request: InferenceRequest):
"""提交请求到批处理队列"""
self.pending_queue.append(request)
# 按优先级排序(VIP 优先)
self.pending_queue.sort(key=lambda r: r.priority)
# 检查是否满足立即批处理条件
if self._should_flush():
self.batch_ready_event.set()
def _should_flush(self) -> bool:
"""判断是否应该立即发送批次"""
if not self.pending_queue:
return False
# 条件 1:队列已满
if len(self.pending_queue) >= self.max_batch_size:
return True
# 条件 2:token 总量达到上限
total_tokens = sum(r.estimated_prefill_tokens for r in self.pending_queue)
if total_tokens >= self.max_total_tokens:
return True
# 条件 3:等待时间超过阈值
oldest_request = self.pending_queue[0]
wait_time_ms = (time.time() - oldest_request.created_at) * 1000
if wait_time_ms >= self.max_wait_ms:
return True
return False
async def get_next_batch(self) -> list[InferenceRequest]:
"""获取下一个批次"""
while not self._should_flush():
self.batch_ready_event.clear()
await self.batch_ready_event.wait()
batch = self.pending_queue[:self.max_batch_size]
self.pending_queue = self.pending_queue[self.max_batch_size:]
return batch
# ── 3. 推理调度器 ──
class InferenceScheduler:
"""推理调度器:管理 PD 分离集群"""
def __init__(self):
self.batcher = DynamicBatcher()
self.prefill_nodes: list[str] = [] # Prefill 节点列表
self.decode_nodes: list[str] = [] # Decode 节点列表
self.node_load: dict[str, int] = {} # 节点负载
async def schedule_request(self, request: InferenceRequest):
"""调度单个请求"""
# 提交到批处理器
await self.batcher.submit(request)
async def run_serving_loop(self):
"""主服务循环"""
while True:
# 获取下一个批次
batch = await self.batcher.get_next_batch()
if not batch:
continue
# 选择 Prefill 节点(负载最低)
prefill_node = self._select_least_loaded_node("prefill")
# 执行 Prefill
kv_cache = await self._execute_prefill(prefill_node, batch)
# 选择 Decode 节点(队列最短)
decode_node = self._select_least_loaded_node("decode")
# 传输 KV Cache 并执行 Decode
results = await self._execute_decode(decode_node, batch, kv_cache)
# 返回结果
for request, result in zip(batch, results):
await self._send_response(request, result)
def _select_least_loaded_node(self, node_type: str) -> str:
"""选择负载最低的节点"""
nodes = self.prefill_nodes if node_type == "prefill" else self.decode_nodes
return min(nodes, key=lambda n: self.node_load.get(n, 0))
async def _execute_prefill(self, node, batch):
"""执行 Prefill 阶段"""
# 实际实现中调用 vLLM/TensorRT-LLM API
pass
async def _execute_decode(self, node, batch, kv_cache):
"""执行 Decode 阶段"""
pass
async def _send_response(self, request, result):
"""发送响应给用户"""
pass
# ── 4. 自动扩缩容 ──
class AutoScaler:
"""根据负载自动扩缩容 GPU 节点"""
def __init__(
self,
scheduler: InferenceScheduler,
min_decode_nodes: int = 2,
max_decode_nodes: int = 32,
target_gpu_utilization: float = 0.7,
scale_up_threshold: float = 0.85, # GPU 利用率 >85% 扩容
scale_down_threshold: float = 0.3, # GPU 利用率 <30% 缩容
cooldown_seconds: int = 300, # 扩缩容冷却时间 5 分钟
):
self.scheduler = scheduler
self.min_nodes = min_decode_nodes
self.max_nodes = max_decode_nodes
self.target_util = target_gpu_utilization
self.scale_up_threshold = scale_up_threshold
self.scale_down_threshold = scale_down_threshold
self.cooldown = cooldown_seconds
self.last_scale_time = 0
async def check_and_scale(self):
"""检查并执行扩缩容"""
current_time = time.time()
# 冷却期内不操作
if current_time - self.last_scale_time < self.cooldown:
return
# 计算平均 GPU 利用率
avg_util = self._get_avg_gpu_utilization()
queue_length = len(self.scheduler.batcher.pending_queue)
current_nodes = len(self.scheduler.decode_nodes)
if avg_util > self.scale_up_threshold and current_nodes < self.max_nodes:
# 扩容
new_nodes = self._calculate_scale_up_count(avg_util, queue_length)
await self._add_decode_nodes(new_nodes)
self.last_scale_time = current_time
print(f"🔼 扩容: +{new_nodes} 节点 (GPU util: {avg_util:.1%})")
elif avg_util < self.scale_down_threshold and current_nodes > self.min_nodes:
# 缩容
remove_count = self._calculate_scale_down_count(avg_util, current_nodes)
await self._remove_decode_nodes(remove_count)
self.last_scale_time = current_time
print(f"🔽 缩容: -{remove_count} 节点 (GPU util: {avg_util:.1%})")
def _get_avg_gpu_utilization(self) -> float:
"""获取平均 GPU 利用率"""
# 实际实现中通过 pynvml 或 DCGM 获取
return 0.0
def _calculate_scale_up_count(self, util, queue_len) -> int:
"""计算需要扩容的节点数"""
# 目标:将利用率降到 target_util
current_capacity = len(self.scheduler.decode_nodes)
needed_capacity = int(current_capacity * util / self.target_util) + 1
return min(needed_capacity - current_capacity, 4) # 每次最多加 4 个
def _calculate_scale_down_count(self, util, current_count) -> int:
"""计算需要缩容的节点数"""
target_capacity = max(int(current_count * util / self.target_util) + 1, self.min_nodes)
return min(current_count - target_capacity, 2) # 每次最多减 2 个
async def _add_decode_nodes(self, count: int):
"""添加 Decode 节点"""
pass
async def _remove_decode_nodes(self, count: int):
"""移除 Decode 节点(优雅关闭,等待当前请求完成)"""
pass💡 一句话理解
动态批处理的最大等待时间(max_wait_ms)是关键参数。对于交互式聊天应用,设置为 5-10ms;对于批量处理任务(如文档摘要),可以设置为 50-100ms 以获得更高的吞吐量。
⚠️ 常见踩坑
自动扩缩容必须设置冷却时间(建议 5 分钟)。没有冷却时间的扩缩容会在负载波动时频繁扩缩,导致 GPU 资源浪费和服务不稳定。
72026 年推理优化前沿与趋势
趋势一:硬件-软件协同优化。NVIDIA Blackwell(B200/B300)架构引入了第二代 Transformer Engine,原生支持 FP4 精度推理——相比 FP8,FP4 可以将推理速度再提升 2 倍,同时保持可接受的精度。这要求推理引擎(TensorRT-LLM、vLLM)在底层适配 FP4 的量化和反量化逻辑。
趋势二:推测预填充(Speculative Prefill)。受投机解码启发,2026 年出现了「推测预填充」技术——在用户还在输入 prompt 时,就开始推测性地处理可能的后续输入。如果推测正确,用户按下发送键时 prompt 已经处理完毕,实现「零延迟」首 token。
趋势三:KV Cache 即服务(KV Cache as a Service)。将 KV Cache 存储在独立的内存集群中(如 Redis、Dragonfly),而非 GPU 显存。这使得 KV Cache 可以在多个推理节点之间共享,支持请求的无缝迁移和故障恢复。
趋势四:端侧推理的突破。高通的 Cloud AI 100 Ultra 和苹果的 M5 Neural Engine 使得 7B-13B 参数的模型可以在端侧以 30+ token/s 的速度运行。2026 年底,端侧推理将覆盖 80% 的智能手机和笔记本电脑。
趋势五:推理成本持续下降。2024 年,GPT-4 级别的模型推理成本约为 $10/百万 token。2026 年,通过全栈优化(量化 + PD 分离 + 投机解码 + 硬件进步),开源 70B 模型的自托管推理成本已降至 $0.3-0.5/百万 token——下降了 20 倍。这意味着 LLM 推理正在从「昂贵的 API 调用」变为「几乎免费的基础设施」。
💡 一句话理解
关注 FP4 推理和端侧推理这两个方向。FP4 将在 2026 下半年成为 NVIDIA 新 GPU 的标配能力;端侧推理将催生大量新的应用场景(离线 AI 助手、隐私敏感任务、低延迟边缘计算)。
⚠️ 常见踩坑
推理优化的「免费午餐」已经吃完了。从 FP16 到 INT8 到 INT4,每次量化带来的收益在递减,而精度损失在递增。不要盲目追求最低精度——根据你的应用场景,找到精度和成本的最佳平衡点。