大模型推理部署实战:从 GPU 显存管理到高并发服务化的全链路设计
大模型推理部署实战:从 GPU 显存管理到高并发服务化的全链路设计

一、Token 吞吐与显存瓶颈:LLM 部署的工程困境
大模型推理部署的核心矛盾在于:GPU 显存是有限的,而模型参数和 KV Cache 的显存需求几乎是无上限的。一个 70B 参数的模型,仅模型权重就需要约 140GB 显存(FP16),加上 KV Cache 和运行时开销,单卡 A100 80GB 根本无法容纳。
更严峻的挑战来自并发。每个并发请求都需要独立的 KV Cache 空间来存储注意力机制的 Key 和 Value。以 Llama-2-70B 为例,单个请求的 KV Cache 在序列长度 2048 时约占 2GB 显存。10 个并发请求就需要 20GB 额外显存,直接挤占模型权重的存储空间。
推理延迟的尾部效应同样不可忽视。Prefill 阶段(首 Token 生成)需要处理整个 Prompt,计算密集;Decode 阶段(后续 Token 生成)每次只处理一个 Token,访存密集。两个阶段的资源瓶颈完全不同,混合调度会导致 GPU 利用率在 30%-90% 之间剧烈波动。
二、LLM 推理引擎的显存管理与调度机制
现代 LLM 推理引擎(vLLM、TGI、TensorRT-LLM)的核心创新在于显存管理和请求调度策略。理解这些机制是优化部署方案的工程基础。
flowchart TB
A[推理请求到达] --> B[请求调度器]
B --> C{Prefill/Decode 分离?}
C -->|是| D[Prefill 实例]
C -->|否| E[混合调度实例]
D --> F[生成 KV Cache]
F --> G[Transfer KV Cache]
G --> H[Decode 实例]
E --> I[连续批处理 Continuous Batching]
H --> J[Token 输出]
I --> J
J --> K{序列结束?}
K -->|是| L[释放 KV Cache]
K -->|否| M[继续 Decode]
M --> I
subgraph PagedAttention 显存管理
N[虚拟 KV Cache 页表] --> O[物理显存块池]
O --> P[按需分配]
O --> Q[共享 Copy-on-Write]
end
style B fill:#e74c3c,color:#fff
style N fill:#3498db,color:#fff
style I fill:#27ae60,color:#fff
PagedAttention 机制:vLLM 的核心创新,借鉴操作系统的虚拟内存分页思想。将 KV Cache 划分为固定大小的 Block(如 16 个 Token 对应一个 Block),维护虚拟 Block 到物理 Block 的页表映射。请求的 KV Cache 不需要连续的显存空间,按需分配物理 Block,序列结束后整块释放。这解决了传统方案中 KV Cache 必须预分配最大长度的显存浪费问题,显存利用率从 20%-40% 提升至 90% 以上。
Continuous Batching:传统 Static Batching 需要等待 Batch 中最长的序列完成后才能释放资源,短序列被迫等待。Continuous Batching 在每个 Decode Step 检查序列是否生成 EOS Token,已完成的序列立即移出 Batch,新请求立即加入。这使得 GPU 利用率不再受限于 Batch 中最慢的请求。
Prefill/Decode 分离调度:将推理拆分为 Prefill 和 Decode 两个独立阶段,分别调度到不同的 GPU 实例。Prefill 实例专注于计算密集的首 Token 生成,可以大 Batch 处理;Decode 实例专注于访存密集的 Token 生成,需要低延迟响应。分离后,两类实例可以独立扩缩容,资源利用率显著提升。
三、LLM 推理服务的高并发部署实现
3.1 基于 vLLM 的推理服务部署
"""
vLLM 推理服务启动配置
关键参数说明:
- gpu_memory_utilization: GPU 显存使用上限,预留空间给 CUDA 内核
- max_num_seqs: 最大并发序列数,受 KV Cache 显存约束
- enable_prefix_caching: 前缀缓存,相同 Prompt 前缀可复用 KV Cache
"""
from vllm import LLM, SamplingParams
from vllm.entrypoints.openai.api_server import run_server
# 推理引擎配置
ENGINE_ARGS = {
"model": "/models/llama-2-70b-chat-hf",
"tensor_parallel_size": 4, # 4 卡张量并行
"gpu_memory_utilization": 0.92, # 显存使用上限 92%
"max_num_seqs": 64, # 最大并发序列数
"max_model_len": 4096, # 最大序列长度
"enable_prefix_caching": True, # 开启前缀缓存
"block_size": 16, # PagedAttention Block 大小
"swap_space": 16, # CPU 交换空间大小(GB)
"dtype": "float16", # 推理精度
}
# 采样参数模板
DEFAULT_SAMPLING = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=2048,
# 为什么设置 frequency_penalty:防止长文本生成中的重复循环
frequency_penalty=0.1,
)
3.2 Kubernetes 上的 GPU 推理服务部署
# GPU 推理服务 Deployment
# 为什么使用 StatefulSet 而非 Deployment:
# 张量并行需要稳定的 Pod 网络标识,Pod 之间通过 RDMA 通信
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: llm-inference
namespace: ai-inference
spec:
replicas: 2 # 2 组 4 卡实例
selector:
matchLabels:
app: llm-inference
template:
metadata:
labels:
app: llm-inference
spec:
# GPU 拓扑感知调度:确保 4 张卡在同一 NUMA 节点
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu-topology
operator: In
values:
- "4x-a100-nvlink"
containers:
- name: vllm-server
image: vllm/vllm-openai:latest
command:
- python
- -m
- vllm.entrypoints.openai.api_server
args:
- --model=/models/llama-2-70b-chat-hf
- --tensor-parallel-size=4
- --gpu-memory-utilization=0.92
- --max-num-seqs=64
- --enable-prefix-caching
resources:
limits:
nvidia.com/gpu: 4
memory: "128Gi"
cpu: "16"
requests:
nvidia.com/gpu: 4
memory: "64Gi"
cpu: "8"
ports:
- containerPort: 8000
# 健康检查:推理服务就绪需要加载模型(约 2 分钟)
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 120
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 180
periodSeconds: 30
---
# HPA:基于 GPU 利用率和请求队列深度自动扩缩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: llm-inference-hpa
namespace: ai-inference
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: StatefulSet
name: llm-inference
minReplicas: 2
maxReplicas: 8
metrics:
# 自定义指标:GPU SM 利用率
- type: Pods
pods:
metric:
name: gpu_sm_utilization
target:
type: AverageValue
averageValue: "70"
# 自定义指标:推理队列深度
- type: Pods
pods:
metric:
name: inference_queue_depth
target:
type: AverageValue
averageValue: "10"
3.3 推理请求网关与负载均衡
package gateway
import (
"context"
"sync/atomic"
)
// LLMGateway 大模型推理网关
// 核心职责:请求路由、负载均衡、流式响应转发
type LLMGateway struct {
backends []*InferenceBackend
// 轮询计数器,使用原子操作避免锁竞争
rrCounter uint64
}
// InferenceBackend 推理后端实例
type InferenceBackend struct {
Address string
ActiveConns int64 // 当前活跃连接数
MaxConns int64 // 最大并发连接数
AvgLatencyMs int64 // 平均推理延迟
QueueDepth int64 // 推理队列深度
IsHealthy bool // 健康状态
}
// SelectBackend 选择推理后端
// 为什么不用简单轮询:推理延迟差异大,轮询会导致长尾请求堆积
// 采用最少活跃连接 + 队列深度加权的策略
func (g *LLMGateway) SelectBackend() (*InferenceBackend, error) {
var best *InferenceBackend
minScore := int64(math.MaxInt64)
for _, b := range g.backends {
if !b.IsHealthy {
continue
}
// 评分 = 活跃连接数 * 2 + 队列深度 * 3
// 队列深度权重更高:排队中的请求对延迟影响更大
score := atomic.LoadInt64(&b.ActiveConns)*2 +
atomic.LoadInt64(&b.QueueDepth)*3
if score < minScore {
minScore = score
best = b
}
}
if best == nil {
return nil, ErrNoHealthyBackend
}
return best, nil
}
// StreamInference 流式推理请求
// 使用 SSE 协议将 Token 逐个推送给客户端
func (g *LLMGateway) StreamInference(ctx context.Context,
prompt string) (<-chan Token, error) {
backend, err := g.SelectBackend()
if err != nil {
return nil, err
}
// 增加后端活跃连接计数
atomic.AddInt64(&backend.ActiveConns, 1)
defer atomic.AddInt64(&backend.ActiveConns, -1)
tokenCh := make(chan Token, 64)
go func() {
defer close(tokenCh)
// 调用后端推理服务的流式接口
stream, err := backend.StreamComplete(ctx, prompt)
if err != nil {
tokenCh <- Token{Error: err}
return
}
for token := range stream {
select {
case tokenCh <- token:
case <-ctx.Done():
// 客户端断开连接,停止接收
return
}
}
}()
return tokenCh, nil
}
四、LLM 部署的代价:显存、延迟与成本的三角约束
显存与并发的零和博弈:GPU 显存是固定资源,模型权重、KV Cache 和运行时开销三者竞争。增加并发数意味着更多 KV Cache,挤占模型权重的显存空间。当显存不足时,推理引擎会触发 Swap(将 KV Cache 换出到 CPU 内存),延迟从毫秒级飙升至秒级。生产环境中,必须根据业务 QPS 和平均序列长度精确计算 KV Cache 需求,避免 Swap 触发。
张量并行的通信开销:4 卡张量并行时,每个 Decode Step 需要一次 All-Reduce 通信。NVLink 互联的通信延迟约 20-50us,PCIe 互联则高达 100-200us。对于 Decode 阶段(单步计算量小),通信开销占比可达 30%-50%。如果模型规模允许,优先选择单卡部署(如量化后的 70B 模型),避免通信开销。
前缀缓存的命中率代价:Prefix Caching 通过复用相同 Prompt 前缀的 KV Cache 来加速推理,但缓存管理本身有开销。Block 的哈希计算和页表查找在每个请求的 Prefill 阶段都需要执行。如果 Prompt 前缀的重复率低于 10%,缓存命中率低,管理开销反而拖慢推理速度。启用前需要分析业务流量的 Prompt 分布特征。
适用边界:上述方案适用于 7B-70B 规模模型的在线推理服务。对于 7B 以下的小模型,单卡部署即可满足大多数场景,无需复杂的并行策略。对于 175B 以上的超大模型,需要引入流水线并行和专家并行,架构复杂度显著提升。对于离线批处理场景,吞吐优先于延迟,应采用大 Batch + Static Batching 策略。
五、总结
LLM 推理部署的核心挑战是 GPU 显存管理和高并发调度。PagedAttention 解决了 KV Cache 的显存碎片问题,Continuous Batching 提升了 GPU 利用率,Prefill/Decode 分离优化了延迟分布。但这些优化都有其适用前提和代价。
落地路线建议:第一步,根据模型规模和 QPS 需求计算 GPU 资源需求,确定并行策略;第二步,部署 vLLM 推理引擎,配置 PagedAttention 和 Continuous Batching;第三步,实现推理网关的负载均衡和流式转发;第四步,配置基于 GPU 利用率的 HPA 自动扩缩容。每一步都需要在真实推理负载下验证参数,特别是显存利用率和 Swap 触发阈值。
更多推荐




所有评论(0)