上一篇用Docker部署推理服务时提到了vLLM,但只是一笔带过。这篇文章深入聊聊vLLM的部署和调优——这是目前吞吐量最高的开源推理框架,没有之一。

PagedAttention:vLLM快的秘密

传统推理框架处理KV Cache时,会在显存中预分配连续的大块内存。问题是:不同请求的序列长度不同,预分配的大小按最大长度来,实际利用率经常不到50%。显存就这么被浪费了;

vLLM的PagedAttention借鉴了操作系统的虚拟内存分页思想:把KV Cache分成固定大小的"页面",按需分配,用完回收。效果是显存利用率大幅提升,同一块显卡能同时处理更多请求;

实际测试数据:在A100 80GB上跑Qwen2.5-7B,HuggingFace Transformers大约20 tokens/s/vLLM达到80+ tokens/s——4倍差距。

安装

pip install vllm
# 验证CUDA可用
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name())"

启动推理服务

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen2.5-7B-Instruct \
    --host 0.0.0.0 \
    --port 8000 \
    --max-model-len 8192 \
    --gpu-memory-utilization 0.9

首次启动会下载模型(如果本地没有的话),加载时间几分钟到十几分钟不等。看到Uvicorn running的消息就表示服务就绪了。

核心参数详解

gpu-memory-utilization控制显存使用比例。0.9表示用90%的显存。设太高可能OOM,设太低浪费显存。建议从0.9开始,遇到OOM就降到0.85;

max-model-len最大上下文长度。设太大占用更多显存。根据实际需求设,不需要128K上下文就别设128K;

tensor-parallel-size多卡张量并行。4卡A100就设4,vLLM会自动把模型拆分到4张卡上;

dtype数据类型。auto自动选择(通常选bfloat16如果硬件支持)。bfloat16精度和float16差不多,但数值稳定性更好;

多卡部署72B模型

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen2.5-72B-Instruct \
    --tensor-parallel-size 4 \
    --max-model-len 4096 \
    --gpu-memory-utilization 0.9

4张A100 80GB可以流畅运行72B模型。注意多卡通信需要NVLink或高速PCIe,不然通信开销会拖慢整体速度。

量化部署

AWQ或GPTQ量化可以把显存需求减半:

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen2.5-7B-Instruct-AWQ \
    --quantization awq \
    --max-model-len 8192

量化后的模型大约节省50%显存,推理速度提升20-30%,精度损失在大多数场景下可以忽略。

投机解码

用小模型快速生成候选token,大模型批量验证。在合适的负载下显著提升生成速度:

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen2.5-7B-Instruct \
    --speculative-model Qwen/Qwen2.5-1.5B-Instruct \
    --num-speculative-tokens 5

监控

vLLM内置Prometheus端点(/metrics),暴露的关键指标包括:vllm:avg_generation_throughput_toks_per_s(生成吞吐量)、vllm:num_requests_running(运行中请求数)、vllm:gpu_cache_usage_perc(KV Cache使用率)、vllm:e2e_request_latency_seconds(端到端延迟)。

写在最后

vLLM把推理性能的天花板抬高了一大截。对于需要处理大量并发请求的生产环境来说,它是当前最佳选择。先用默认参数跑起来,再根据监控数据逐步调优——不要一开始就追求极致参数,稳定比极致更重要。