LLM推理系统基础
1. 概述
大语言模型(Large Language Model, LLM)推理系统是部署深度学习模型进行推理的核心基础设施。与训练阶段不同,推理阶段对延迟和吞吐量有严格的要求,且通常需要在有限的计算资源下服务大量用户请求。
本章将系统性地介绍LLM推理系统的核心概念、架构设计、性能指标和优化策略。
2. 自回归生成机制
现代LLM基于Transformer架构,其推理过程可分为两个阶段:
预填充阶段(Prefill Phase)
- 处理输入prompt,计算所有token的key-value缓存
- 主要计算密集型操作
- 输出第一个新token
解码阶段(Decode Phase)
- 自回归生成新token
- 每步生成一个token,需使用之前所有token的KV Cache
- 主要受内存带宽限制(Memory-Bound)
def transformer_inference(model, input_ids, max_new_tokens):
# Prefill阶段
kv_cache = []
hidden_states = model.embed_tokens(input_ids)
for layer in model.layers:
hidden_states, kv = layer(hidden_states, use_cache=True)
kv_cache.append(kv)
logits = model.norm(hidden_states) @ model.lm_head.weight.T
next_token = logits.argmax(dim=-1, keepdim=True)
# Decode阶段(自回归)
for _ in range(max_new_tokens):
# 拼接历史KV Cache
hidden_states = model.embed_tokens(next_token)
for i, layer in enumerate(model.layers):
kv_prev = kv_cache[i]
hidden_states, kv = layer(
hidden_states,
past_key_values=kv_prev,
use_cache=True
)
kv_cache[i] = kv
logits = model.norm(hidden_states) @ model.lm_head.weight.T
next_token = logits.argmax(dim=-1, keepdim=True)
if next_token == model.eos_token_id:
break
return input_ids + generated_tokens
2.2 计算特性分析
| 阶段 | 计算模式 | 瓶颈 | 优化重点 |
|---|
| Prefill | 并行 | 计算密集 | 算子融合、并行化 |
| Decode | 顺序 | 内存带宽 | KV Cache管理、批处理 |
3. 推理系统核心指标
3.1 延迟指标
| 指标 | 定义 | 优化目标 |
|---|
| Time to First Token (TTFT) | 首token生成时间 | Prefill加速 |
| Time per Output Token (TPOT) | 每token平均时间 | Decode加速 |
| End-to-End Latency | 完整生成时间 | 端到端优化 |
3.2 吞吐量指标
| 指标 | 定义 | 优化目标 |
|---|
| Requests Per Second (RPS) | 每秒请求数 | 系统吞吐量 |
| Tokens Per Second (TPS) | 每秒生成token数 | 生成效率 |
| Throughput | 单位时间处理数据量 | 资源利用 |
3.3 资源指标
| 指标 | 定义 | 优化目标 |
|---|
| GPU Memory | GPU显存占用 | 内存优化 |
| Batch Size | 批处理大小 | 动态调整 |
| Model Parallelism | 模型并行度 | 并行策略 |
4. 推理系统架构
4.1 典型架构组件
┌─────────────────────────────────────────────────────────────┐
│ Client Requests │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ API Gateway / Scheduler │
│ (请求路由、负载均衡、速率限制) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Inference Engine │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Tokenizer │ │ Model │ │ Detokenizer │ │
│ │ │ │ Executor │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ GPU Cluster │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Model Parallelism: TP / PP / EP │ │
│ │ Memory Optimization: KV Cache / Quantization │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
4.2 模型并行策略
张量并行(Tensor Parallelism, TP)
- 将单一层的参数分割到多个GPU
- 适用于单层计算密集型操作
- 需要高速互连(NVLink)
class TensorParallelLayer(nn.Module):
def __init__(self, world_size):
super().__init__()
self.world_size = world_size
self.rank = dist.get_rank()
# 按列分割lm_head
self.lm_head = ColumnParallelLinear(
hidden_size, vocab_size,
num_streams=self.world_size
)
def forward(self, x):
# 本地计算
local_output = self.lm_head(x)
# AllReduce汇总
output = [torch.zeros_like(local_output) for _ in range(self.world_size)]
dist.all_gather(output, local_output)
return torch.stack(output, dim=0).sum(dim=0)
流水线并行(Pipeline Parallelism, PP)
- 将不同层分配到不同GPU
- 减少通信开销
- 存在流水线气泡(P Bubble)
专家并行(Expert Parallelism, EP)
- 仅适用于MoE模型
- 不同专家分配到不同GPU
- 需要路由机制协调
4.3 内存层级
GPU HBM (High Bandwidth Memory)
├── Model Weights (fp16/bf16)
├── KV Cache
│ ├── Prefill KV
│ └── Decode KV (动态增长)
├── Activations (中间计算结果)
└── Temporary Buffers
CPU Memory (Host)
├── Disk Cache (冷启动优化)
└── Request Queue
5. KV Cache管理
5.1 KV Cache问题
自回归生成中,每个解码步骤都需要访问全部历史KV Cache:
- 内存爆炸:KV Cache随序列长度线性增长
- 带宽瓶颈:每次访问完整历史导致带宽压力
- 容量限制:有限显存限制最大并发
5.2 解决方案概览
| 方法 | 核心思想 | 优缺点 |
|---|
| 量化 | 降低KV Cache精度 | 效果好,但有精度损失 |
| 压缩 | 丢弃不重要KV | 需要判断重要性 |
| 缓存 | 多级缓存策略 | 需硬件支持 |
| 分离 | 冷热Token分离 | 需算法配合 |
6. 推理优化技术
6.1 批处理优化
静态批处理
连续批处理(Continuous Batching)
- 动态批处理
- 最大化GPU利用率
- 引入新请求替换完成请求
class ContinuousBatcher:
def __init__(self, max_batch_size):
self.max_batch_size = max_batch_size
self.running_requests = []
self.pending_requests = []
def step(self, model):
# 1. 添加新请求到running
while (len(self.running_requests) < self.max_batch_size
and self.pending_requests):
self.running_requests.append(self.pending_requests.pop(0))
# 2. 批量推理
batch = self.prepare_batch(self.running_requests)
outputs = model(batch)
# 3. 处理完成请求
completed = []
remaining = []
for req, output in zip(self.running_requests, outputs):
req.generated_ids.append(output)
if self.is_finished(req):
completed.append(req)
else:
remaining.append(req)
self.running_requests = remaining
return completed
6.2 算子融合
将多个独立算子合并为单一kernel,减少内存访问:
# 原始计算
x → LayerNorm → Linear → SiLU → Dropout → Linear
# 融合后
x → FusedLayerNorm → FusedMLP → Linear
6.3 量化推理
| 精度 | 内存缩减 | 速度提升 | 精度损失 |
|---|
| fp16 | 1x | 1x | 基准 |
| bf16 | 1x | ~1x | 极小 |
| int8 | 2x | 1.5-2x | 可接受 |
| int4 | 4x | 2-4x | 需校准 |
7. 主流推理框架对比
| 框架 | 开发方 | 核心特性 | 适用场景 |
|---|
| vLLM | UC Berkeley | PagedAttention、连续批处理 | 通用推理 |
| TensorRT-LLM | NVIDIA | TensorRT优化、INT8/FP8 | 生产部署 |
| llama.cpp | Georgi Gerganov | 量化、CPU推理 | 本地部署 |
| DeepSpeed-Inference | Microsoft | ZeRO、Transformer优化 | 大模型 |
| SGLang | LMSYS | RadixAttention、调度优化 | 高吞吐 |
| TGI | HuggingFace | 量化为、推理优化 | 灵活部署 |
8. 实践建议
8.1 延迟优化
- 启用KV Cache量化(INT8/FP8)
- 使用PagedAttention管理内存
- 预填充阶段使用更大batch
- 考虑投机解码加速
8.2 吞吐量优化
- 启用连续批处理
- 使用更长的预填充chunk
- 动态调整batch size
- 多级并行策略
8.3 资源优化
- 根据显存选择合适的量化精度
- 使用INT4权重 + INT8 KV Cache组合
- 启用FlashAttention
- 优化请求调度策略
9. 参考文献