背景
前一张显卡是48GB,运行 Qwen/Qwen3-30B-A3B-Instruct-2507-fp8模型。在我换到5090显卡后,显存就不足以运行原先的模型了。
为了充分利用好5090显卡的显存容量,我选择了下述两个模型:
models/Qwen--Qwen3-30B-A3B-GPTQ-Int4models/cpatonn--Qwen3-30B-A3B-Instruct-2507-AWQ-4bit
这两个模型的磁盘占用均不到20GB,然而我在使用运行模型的过程中均遇到了OOM,显存空间不够的崩溃。按理说32G的显存应该够用。我发现模型在推理的过程中除了要保存模型的参数,还要保存KV cache一些中间状态所以就导致了显存不够。解决方案就是设置降低模型的token长度,就可解决。
VLLM
推理脚本
vLLM 采用了一种叫做 PagedAttention 的技术,它会像操作系统管理内存一样,将 GPU 显存划分为一个个固定大小的 "页面"。
在推理开始前,vLLM 会根据你设定的 max_model_len 和 batch_size,一次性地、预先分配好 足够容纳所有可能 token 的 KV Cache 显存空间。它会假设每个序列都会达到 max_model_len 的长度,并按此最大值来预留空间。
对短输入的影响
即使你的输入序列很短(比如只有 100 个 token),vLLM 依然已经为它在显存中预留了 max_model_len 个位置。
内存带宽开销:虽然实际只使用了 100 个位置,但 GPU 在访问这些缓存时,其内存控制器的访问模式和带宽占用,在某种程度上依然受到为 max_model_len 预分配的大块连续内存的影响。管理一个巨大的、大部分是空的缓存,本身就比管理一个大小合适的缓存要消耗更多的 "管理成本"。
并行计算效率:GPU 是为并行计算设计的。当 max_model_len 很大时,即使实际数据很短,一些为处理长序列而优化的并行计算单元(如 Tensor Cores)可能无法被充分利用,或者需要执行一些额外的逻辑来处理 "空" 的数据,从而影响了整体效率。
现代深度学习框架(如 PyTorch/TensorFlow)在执行计算时,会先构建一个 "计算图",它定义了所有操作的执行顺序和依赖关系。
- 静态计算图:vLLM 为了追求极致性能,会在启动时根据 max_model_len 等参数,预先构建并 "固化" 一个最优的计算图。这个图一旦生成,在整个推理过程中就不会再改变。
- 长序列的计算图开销:一个为 max_model_len=8192 构建的计算图,其内部的矩阵乘法、循环等操作的维度都是按照 8192 来优化的。当你用这个图去处理一个很短的序列时,这些为长序列设计的计算步骤中,有一部分就成了 "多余" 的开销。
- 更优的短序列计算图:当你将 max_model_len 降低到 2048,vLLM 会构建一个全新的、为 2048 长度优化的计算图。这个新图的计算步骤更紧凑,循环次数更少,内存访问模式更高效,因此即使对于短输入,执行这个 "小而美" 的计算图也会比执行那个 "大而全" 的计算图要快得多。
max_model_len:规定了模型在单次推理过程中所能处理的输入序列长度和输出序列长度之和的上限。设置小一点,显存是够用的。
python
from vllm import LLM, SamplingParams
prompts = [
"Hello, who are you?",
"The president of the United States is",
"The capital of France is",
"The future of AI is",
]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
# model_name = "cpatonn/Qwen3-30B-A3B-Instruct-2507-AWQ-4bit"
model_name = "Qwen/Qwen3-30B-A3B-GPTQ-Int4"
llm = LLM(
model=model_name,
gpu_memory_utilization=0.9,
# max_num_seqs=32,
# max_model_len=8192
max_model_len=2048
)
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
prompt = output.prompt
generated_text = output.outputs[0].text
print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")
能成功运行,显存占用也达到了31.1G,再多一点,显存也不够用了。
API部署
同样也需要指定max_model_len参数
vllm serve Qwen/Qwen3-30B-A3B-GPTQ-Int4 --max_model_len 512
langchain调用API
python
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 1. 配置 LLM
# 使用 ChatOpenAI 并指向本地的 vLLM OpenAI 兼容接口
llm = ChatOpenAI(
# model="Qwen/Qwen3-30B-A3B-GPTQ-Int4", # 与实际模型对应
model="cpatonn/Qwen3-30B-A3B-Instruct-2507-AWQ-4bit", # 与实际模型对应
base_url="http://localhost:8000/v1", # 关键:指向本地 vLLM API 服务
api_key="dummy_key", # vLLM 服务通常不需要真实密钥,填写任意非空字符串即可
temperature=0.7,
max_tokens=2025, # 需要比设置的2048小一点
# max_completion_tokens=128
)
# 2. 创建 Prompt
# 使用新的 ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个 helpful 的助手。"),
("user", "请用中文回答以下问题: {question}")
])
# 3. 创建输出解析器
output_parser = StrOutputParser()
# 4. 使用 LCEL 语法创建 Chain
chain = prompt | llm | output_parser
# 5. 运行 Chain
response = chain.invoke({"question": "什么是大语言模型?"})
print(response)