某金融科技公司上线了智能投顾服务,初期用 Flask + HuggingFace Transformers 包装模型 API,单机 QPS 仅 15,P99 延迟 8 秒,高峰期请求大量超时。切换到 vLLM + Continuous Batching 后,同等硬件下 QPS 提升至 180,P99 延迟降至 1.2 秒,且支持动态扩缩容应对流量波动。
模型训练完成只是起点,如何将模型高效、稳定地服务化,才是 LLM 落地的关键工程挑战。
一、推理框架选型
1. 为什么不能用 HuggingFace Transformers 直接服务
HuggingFace Transformers 是研究和实验的标准工具,但直接用于生产服务存在严重问题:
- 显存碎片化:为每个请求预分配固定大小的 KV Cache,大量显存被浪费
- 静态批处理:同一 batch 中的请求必须等最慢的完成才能处理下一批
- 无量化支持:只能用 FP16/BF16 推理,显存占用大
- 单请求串行:无法充分利用 GPU 并行能力
2. 主流推理框架对比
| 框架 | 核心优化 | 量化支持 | 吞吐量 | 易用性 |
|---|---|---|---|---|
| vLLM | PagedAttention, Continuous Batching | AWQ, GPTQ | 高 | 高 |
| TGI | FlashAttention, Continuous Batching | bitsandbytes | 中高 | 高 |
| TensorRT-LLM | Kernel 融合, CUDA 优化 | FP8, INT8 | 极高 | 低 |
| SGLang | RadixAttention, 连续批处理 | FP8, GPTQ | 高 | 中 |
| LMDeploy | TurboMind 引擎 | AWQ, W4A16 | 高 | 中 |
3. 选型决策
追求易用性和快速上线 → vLLM追求极致性能(NVIDIA GPU)→ TensorRT-LLMHuggingFace 生态深度用户 → TGI需要前缀缓存和 RadixAttention → SGLang二、vLLM
1. PagedAttention
vLLM 的核心创新是 PagedAttention,借鉴操作系统虚拟内存分页机制管理 KV Cache:
传统方式: 为每个请求预分配最大长度的连续 KV Cache → 显存利用率低(约 20-40%),碎片严重
PagedAttention: 将 KV Cache 分成固定大小的块(block),按需分配 → 显存利用率接近 100%,支持更多并发请求# vLLM 初始化from vllm import LLM, SamplingParams
llm = LLM( model="meta-llama/Llama-3-8B-Instruct", tensor_parallel_size=2, # 2 卡张量并行 gpu_memory_utilization=0.9, # GPU 显存使用率 max_model_len=8192, # 最大序列长度 enforce_eager=False, # 启用 CUDA Graph 优化)2. Continuous Batching
传统静态批处理中,先完成的请求必须等待最慢的请求,GPU 利用率低。Continuous Batching(连续批处理)在请求完成后立即将其移出 batch,新请求即时填入:
静态批处理: Batch [Req1, Req2, Req3] → 等最慢完成 → 下一批 Req1 先完成但空等,GPU 闲置
Continuous Batching: 时刻 t: [Req1, Req2, Req3] 时刻 t+1: [Req2, Req3, Req4] ← Req1 完成,Req4 立即填入 GPU 始终满载3. 部署配置
# vLLM 服务启动配置host: 0.0.0.0port: 8000model: meta-llama/Llama-3-8B-Instructtensor-parallel-size: 2gpu-memory-utilization: 0.9max-num-batched-tokens: 8192max-num-seqs: 256quantization: awqmax-model-len: 8192# 启动 vLLM OpenAI 兼容 API 服务python -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-3-8B-Instruct \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.9 \ --quantization awq \ --max-model-len 8192三、TGI
1. 核心特性
TGI(Text Generation Inference)是 HuggingFace 官方的推理服务框架:
| 特性 | 说明 |
|---|---|
| FlashAttention | 高效注意力计算,降低显存占用 |
| Continuous Batching | 持续批处理,提升吞吐量 |
| Speculative Decoding | 推测解码加速推理 |
| Watermark | 模型输出水印,检测生成内容 |
| OpenAI 兼容 API | 无缝替换 OpenAI 接口 |
2. Docker 部署
docker run --gpus all -p 8080:80 \ -v $PWD/data:/data \ ghcr.io/huggingface/text-generation-inference:latest \ --model-id meta-llama/Llama-3-8B-Instruct \ --quantize bitsandbytes-nf4 \ --max-input-length 2048 \ --max-total-tokens 4096 \ --cuda-graph3. 客户端调用
from openai import OpenAI
# TGI 兼容 OpenAI APIclient = OpenAI( base_url="http://localhost:8080/v1", api_key="not-needed",)
response = client.chat.completions.create( model="meta-llama/Llama-3-8B-Instruct", messages=[ {"role": "user", "content": "解释量子计算的基本原理"}, ], max_tokens=512, temperature=0.7,)四、TensorRT-LLM
1. 极致优化原理
TensorRT-LLM 是 NVIDIA 官方的推理优化框架,通过多层编译优化实现极致性能:
- Kernel 融合:将多个算子合并为一个 CUDA Kernel,减少显存读写
- 自动调优:针对目标 GPU 自动选择最优 Kernel 配置
- FP8 量化:利用 H100 的 FP8 硬件加速
- KV Cache 量化:压缩 KV Cache,支持更长上下文
2. 构建与部署
from tensorrt_llm import LLM, BuildConfig
build_config = BuildConfig( max_batch_size=128, max_input_len=2048, max_output_len=512, quantization="fp8", enable_context_fmha=True, # FlashAttention)
llm = LLM( model="meta-llama/Llama-3-8B-Instruct", build_config=build_config, tp_size=2,)TensorRT-LLM 的 Engine 构建过程较慢(可能需要数十分钟),但构建完成后推理性能极佳。适合模型固定、追求极致性能的生产场景。模型更新时需要重新构建 Engine。
五、生产部署架构
1. 部署架构选择
| 模型规模 | 并行策略 | 推荐框架 | 硬件需求 |
|---|---|---|---|
| 1-3B | 单卡 | vLLM, TGI | 1 × A10G |
| 7B | 单卡/双卡 TP | vLLM, TGI | 1-2 × A100 |
| 13B | 双卡 TP | vLLM, TensorRT-LLM | 2 × A100 |
| 70B | 4-8 卡 TP | vLLM, TensorRT-LLM | 4-8 × H100 |
| 100B+ | TP + PP | TensorRT-LLM | 8+ × H100 |
2. 负载均衡与扩缩容
class LLMGateway: """LLM 推理服务网关:负载均衡 + 健康检查"""
def __init__(self, backends: list): self.backends = backends
def route_request(self, request): # 基于负载的智能路由 healthy = [b for b in self.backends if b.is_healthy()] if not healthy: raise ServiceUnavailable("No healthy backend")
# 选择当前负载最低的后端 backend = min(healthy, key=lambda b: b.current_load()) return self.forward(backend, request)
def forward(self, backend, request): try: return backend.generate( prompt=request.prompt, max_tokens=request.max_tokens, temperature=request.temperature, ) except Exception as e: backend.mark_unhealthy() raise3. Kubernetes 部署
apiVersion: apps/v1kind: Deploymentmetadata: name: llm-inferencespec: replicas: 3 template: spec: containers: - name: vllm image: vllm/vllm-openai:latest resources: limits: nvidia.com/gpu: 2 memory: "64Gi" env: - name: VLLM_MODEL value: "meta-llama/Llama-3-8B-Instruct" - name: VLLM_TP value: "2"4. 性能监控
| 指标 | 说明 | 告警阈值 |
|---|---|---|
| 请求延迟 P99 | 推理请求最大延迟 | > 3s |
| 首 Token 延迟 TTFT | 首 Token 返回时间 | > 500ms |
| 吞吐量 QPS | 每秒处理请求数 | < 预期 50% |
| GPU 利用率 | GPU 计算资源使用率 | < 60% |
| KV Cache 利用率 | KV Cache 块使用率 | > 90% |
| 请求排队数 | 等待处理的请求数 | > 100 |
六、多模型服务
1. 模型路由
生产环境中往往需要同时服务多个模型,根据请求特征路由到不同模型:
class ModelRouter: """基于请求特征的模型路由"""
def route(self, request): # 简单问题路由到小模型,复杂问题路由到大模型 if request.estimated_complexity == "simple": return self.get_model("llama-3-8b") elif request.estimated_complexity == "complex": return self.get_model("gpt-4")
# 按成本预算路由 if request.budget == "low": return self.get_model("llama-3-8b-awq")
return self.get_model("llama-3-70b")2. 模型热切换
LoRA 微调的模型支持动态加载切换,无需重启服务:
# vLLM 多 LoRA 服务llm = LLM( model="meta-llama/Llama-3-8B-Instruct", enable_lora=True, max_loras=4, # 同时加载 4 个 LoRA max_lora_rank=16,)
# 请求时指定 LoRAresponse = client.chat.completions.create( model="meta-llama/Llama-3-8B-Instruct", messages=[...], extra_body={"lora_name": "legal-lora"}, # 使用法律领域 LoRA)七、服务化实践清单
| 实践项 | 说明 | 优先级 |
|---|---|---|
| 选择推理框架 | 根据性能需求和团队能力选择 | 高 |
| 配置 Continuous Batching | 提升吞吐量的核心手段 | 高 |
| 启用量化 | 降低显存占用和推理成本 | 高 |
| 设置健康检查 | 保证服务可用性 | 高 |
| 配置负载均衡 | 支持水平扩展 | 中 |
| 实现模型路由 | 多模型协同服务 | 中 |
| 监控指标埋点 | 延迟、吞吐、GPU 利用率 | 中 |
| 设置限流熔断 | 防止过载导致雪崩 | 中 |
| KV Cache 监控 | 避免显存不足影响服务 | 低 |
模型服务化是 LLM 从实验走向生产的最后一公里。选对推理框架、配好批处理和量化、做好监控和扩缩容——这三步决定了推理服务的成本、延迟和稳定性。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






