大模型推理性能瓶颈在哪里
大模型部署上线后,推理延迟和吞吐量是横在工程团队面前的两座大山。一个7B参数的模型在单卡A100上做自回归生成,如果不做任何优化,首token延迟可能超过500ms,每秒生成的token数不到20。这个数字离生产环境可用的标准差距明显——实时对话场景要求首token延迟低于100ms,批量推理场景则要求吞吐量撑住QPS峰值。
推理性能瓶颈可以拆解为三个维度:显存带宽(权重搬运和KV Cache读写占大头)、计算利用率(decode阶段矩阵乘法是batch维度极小的GEMM,算力利用率常低于10%)、调度策略(不同长度请求混排导致padding浪费)。
理解瓶颈的分布比盲目加卡更重要。用NVIDIA Nsight Systems抓一次推理trace,你会发现decode阶段超过70%的时间花在显存读写上,而不是计算本身。这意味着减少显存访问次数的优化收益远大于增加计算量。
KV Cache优化:从显存占用入手
KV Cache是大模型推理中最大的显存消费者。以Llama-2-7B为例,FP16精度下模型权重约14GB,但2048序列长度的KV Cache就要占掉约2.3GB(32层×2×2048×128×2bytes×batch_size)。batch_size上到32,光KV Cache就吃掉70GB+。
PagedAttention是vLLM的核心创新,它把KV Cache的显存管理从预分配改为按需分配,类似操作系统的虚拟内存分页机制。每个请求的KV Cache不再需要预留最大序列长度的连续显存,而是按block(通常16个token一个block)动态分配。这让显存利用率从40%-60%提升到90%以上,同等显存下可支撑的并发请求数直接翻倍。
配置vLLM的PagedAttention时,关键参数是gpu_memory_utilization,默认0.9,建议根据显卡实际可用显存微调:
from vllm import LLM, SamplingParams
llm = LLM(
model="/data/models/llama-2-7b-chat-hf",
gpu_memory_utilization=0.92, # 留8%给CUDA上下文和临时缓冲
max_model_len=4096, # 限制最大序列长度,减少预分配
enforce_eager=True, # 关闭CUDA Graph,调试阶段方便
)
params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512,
)
MQA/GQA从模型结构层面压缩KV Cache。Multi-Query Attention让所有head共享同一组K和V,Grouped-Query Attention则是折中方案——几个head共享一组KV。Llama-2-70B用的是GQA(8组KV对应64个query head),相比标准MHA,KV Cache体积缩小8倍,decode阶段的显存带宽压力大幅下降。
如果模型已经训练好了不支持GQA,还有一条路:KV Cache量化。把KV Cache从FP16量化到INT8或FP8,体积减半,精度损失在大多数生成任务上可以接受。vLLM目前支持KV Cache FP8:
llm = LLM(
model="/data/models/llama-2-7b-chat-hf",
kv_cache_dtype="fp8_e5m2", # KV Cache使用FP8存储
gpu_memory_utilization=0.9,
)
连续批处理:打破padding浪费
传统的static batching把同一批次的请求pad到最大序列长度。4个请求,生成token数分别是50、100、300、500,batch要在第500个token才结束。前三个请求早就生成了<eos>还在那里占位空跑。
Continuous Batching(也叫inflight batching)的核心思路是:请求生成完毕后立即从batch中移除,新请求实时插入空位。配合iteration-level scheduling,每个解码步都重新计算哪些请求还在活跃。
TGI(Text Generation Inferences)和vLLM都实现了continuous batching。部署时重点关注两个参数:
# vLLM启动参数
python -m vllm.entrypoints.openai.api_server --model /data/models/llama-2-7b-chat-hf --max-num-seqs 128 \ # 单batch最大序列数
--max-num-batched-tokens 8192 \ # 单iteration最大token数
--scheduler-policy fcfs \ # 先来先服务
--gpu-memory-utilization 0.9
max-num-batched-tokens控制每个iteration处理的token总量上限。设置太低浪费算力,太高可能导致延迟毛刺。经验值是设为max_num_seqs × average_output_length的1.2倍左右。
投机解码:用小模型加速大模型
投机解码(Speculative Decoding)用一个小模型(draft model)先预测接下来K个token,然后让大模型(target model)一次forward验证这K个token。验证通过的token直接输出,验证失败的从失败位置重新生成。
理论加速比 = 1 / (1 – acceptance_rate)。如果小模型和大模型的分布足够接近,acceptance_rate在0.7-0.9之间,加速比可达3-5倍,而且输出分布和大模型单独生成完全一致——这是投机解码最关键的性质。
from vllm import LLM
llm = LLM(
model="/data/models/llama-2-70b-chat-hf",
speculative_model="/data/models/llama-2-7b-chat-hf", # draft model
num_speculative_tokens=5, # 每次预测5个token
speculative_max_model_len=4096,
gpu_memory_utilization=0.9,
)
draft model的选择很关键:太小了acceptance_rate低,加速效果差;太大了验证开销大。经验上,draft model参数量是target model的1/10到1/5效果最好。Llama-2-7B做Llama-2-70B的draft model是典型搭配。
量化部署:INT8和AWQ的工程实践
量化是降低显存占用和提升推理速度的最直接手段。GPTQ和AWQ是两种主流的post-training量化方案。
AWQ(Activation-aware Weight Quantization)的思路是:不是所有权重bit位同等重要,protect那些对应大activation值的权重通道。INT4 AWQ量化后,7B模型权重从14GB降到约4GB,70B从140GB降到约40GB,可用消费级显卡部署。
# 使用AutoAWQ量化
from awq import AutoAWQForCausalLM
model = AutoAWQForCausalLM.from_pretrained("/data/models/llama-2-7b-hf")
tokenizer = AutoTokenizer.from_pretrained("/data/models/llama-2-7b-hf", trust_remote_code=True)
quant_config = {
"zero_point": True,
"q_group_size": 128,
"w_bit": 4,
"version": "GEMM"
}
model.quantize(tokenizer, quant_config=quant_config)
model.save_quantized("/data/models/llama-2-7b-hf-awq-int4")
部署时vLLM直接加载AWQ模型:
python -m vllm.entrypoints.openai.api_server --model /data/models/llama-2-7b-hf-awq-int4 --quantization awq --gpu-memory-utilization 0.85
INT4 AWQ的精度损失在大多数场景可接受。但要注意长文本摘要和代码生成对量化更敏感——如果量化后效果下降明显,考虑INT8 AWQ或者GPTQ-A8W8方案。
推理监控与性能调优清单
上线后的推理服务需要持续监控。关键指标:
– TTFT(Time To First Token):反映prefill速度,受batch调度影响大
– TPOT(Time Per Output Token):反映decode速度,受显存带宽和batch size影响
– Throughput(tokens/s/GPU):衡量硬件利用率
– Request Queue Length:排队长度,持续增长说明吞吐不足
用Prometheus + Grafana搭建监控。vLLM原生暴露/metrics端点,直接接入即可:
# prometheus.yml
scrape_configs:
- job_name: 'vllm'
static_configs:
- targets: ['localhost:8000']
metrics_path: /metrics
调优路径:
1. 先确定目标指标——延迟优先还是吞吐优先
2. 延迟优先:减小max-num-seqs,启用speculative decoding
3. 吞吐优先:增大max-num-seqs和max-num-batched-tokens,启用KV Cache FP8
4. 两者都差:检查模型量化是否精度损失过大,考虑换AWQ INT8或升级硬件
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/da-mo-xing-tui-li-xing-neng-you-hua-shi-zhan-cong-kvcache/