大模型推理性能优化实战:从KV Cache到连续批处理的全链路调优

大模型推理性能瓶颈在哪里

大模型部署上线后,推理延迟和吞吐量是横在工程团队面前的两座大山。一个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-seqsmax-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/

(0)
小编小编
上一篇 17小时前
下一篇 17小时前

相关推荐