关键词:RTX 4090 本地大模型、vLLM 部署、Qwen3-30B-A3B、DeepSeek-R1-Distill-32B、AWQ INT4 量化、24G 显存、MoE vs Dense
本文是一次可复现的真机实测:一张 4090(24G)上用 vLLM 跑 5 个 30B 级开源模型,给出吞吐/显存/成本的硬数据,以及一路踩过的 6 个坑。所有脚本与命令均附在文内。
0. 为什么是「30B + INT4」这一档
24G 显存决定了消费级单卡的模型上限。30B 级模型不同精度的显存占用:
| 精度 | 30B 权重占用 | 24G 能否跑 |
|---|---|---|
| BF16 | ~60 GB | ❌ |
| FP8 | ~30 GB | ❌ |
| INT4 (AWQ/GPTQ) | 16--19 GB | ✅ 留 ~4G 给 KV cache |
所以 4090 的甜点区就是 30B 级 + INT4 量化。再大(如 GLM-4.5-Air 106B)即便 INT4 也要 50G+,单卡放不下。
1. 环境与模型清单
bash
# 环境(关键版本)
conda create -n vllm python=3.11 -y && conda activate vllm
pip install vllm # 实测 vllm 0.23.0 / torch 2.11.0+cu130
python -c "import vllm, torch; print(vllm.__version__, torch.cuda.is_available())"
下载(HuggingFace 国内必走镜像,且 HF 不支持 socks 代理,下载前要 unset 所有代理):
bash
export HF_ENDPOINT=https://hf-mirror.com HF_HUB_ENABLE_HF_TRANSFER=1
unset http_proxy https_proxy all_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY
hf download cpatonn/Qwen3-30B-A3B-Instruct-2507-AWQ --local-dir /mnt/sda/llm-bench/qwen3-30b-a3b-awq
# 其余同理:Qwen/Qwen3-32B-AWQ、casperhansen/deepseek-r1-distill-qwen-32b-awq、
# cpatonn/Qwen3-Coder-30B-A3B-Instruct-AWQ、ISTA-DASLab/gemma-3-27b-it-GPTQ-4b-128g
5 个模型:
| 模型 | 架构 | 量化 | 磁盘 | 许可 |
|---|---|---|---|---|
| Qwen3-30B-A3B-Instruct-2507 | MoE 30.5B/3.3B | AWQ | 18.1G | Apache 2.0 |
| Qwen3-Coder-30B-A3B | MoE 30.5B/3.3B | AWQ | 18.1G | Apache 2.0 |
| Qwen3-32B | Dense 32B | AWQ | 19.3G | Apache 2.0 |
| DeepSeek-R1-Distill-Qwen-32B | Dense 32B | AWQ | 19.3G | MIT |
| Gemma-3-27B-it | Dense 27B | GPTQ | 16.9G | Gemma |
2. 实测结果(硬数据)
| 模型 | 单流 decode | 并发16 吞吐 | 净显存 | 上下文 |
|---|---|---|---|---|
| Qwen3-30B-A3B (MoE) | 194.6 tok/s | 1907 tok/s | 21.3G | 8192 |
| Qwen3-Coder-30B (MoE) | 189.5 tok/s | 1928 tok/s | 21.4G | 8192 |
| DeepSeek-R1-Distill-32B | 44.5 tok/s | 394 tok/s | 21.5G | 4096 |
| Qwen3-32B | 43.5 tok/s | 396 tok/s | 22.0G | 4096 |
| Gemma-3-27B (GPTQ) | 35.1 tok/s | 191 tok/s | 21.5G | 2048 |
结论一目了然:两个 MoE 模型的吞吐是 dense 32B 的 4--5 倍。 原因是 Qwen3-30B-A3B 这类 MoE 有 128 个专家、每 token 只激活 8 个(约 3.3B),权重全进显存(照样吃 18G)但单步算力只相当于 3B 小模型------「显存吃满、速度飞起」。
3. 六个坑(这部分才是本文价值)
坑 1:flashinfer 采样 JIT 编译失败
RuntimeError: Could not find nvcc and default cuda_home='/usr/local/cuda' doesn't exist
本机没装 CUDA toolkit(无 nvcc),vLLM 默认的 flashinfer top-k/top-p 采样要运行时 JIT 编译 CUDA kernel → 崩。解法:
python
os.environ["VLLM_USE_FLASHINFER_SAMPLER"] = "0" # 走原生 PyTorch 采样
注意力 kernel 是预编译 cubin,不受影响,性能几乎无损。
坑 2:dense 32B 的 KV cache 被 CUDA Graph 挤没
ValueError: No available memory for the cache blocks.
19G 权重 + 桌面占用下,CUDA Graph 的预留把 KV cache 顶到 0。解法 :dense 模型 enforce_eager=True(关 Graph,省出显存给 KV)。代价是损失一点解码速度------但 dense 本就慢,无所谓。
坑 3:gpu_memory_utilization 要按「空闲」算,不是「总量」
ValueError: Free memory on device (21.53/23.51 GiB) is less than desired GPU memory utilization (0.95, 22.33 GiB)
桌面/其它进程占着 1--2G,util × 总显存 必须 ≤ 空闲。动态算:
python
util = max(0.70, min(0.90, (free_mib - 700) / total_mib))
坑 4:Gemma-3 的 26 万词表吃显存
Gemma3 词表 262144,embedding/lm_head 不量化,吃掉额外 2--3G,KV 只剩 1.2G,4096 上下文放不下(报 estimated maximum model length is 2240)。解法 :max_model_len=2048。
坑 5:vLLM v1 的 RequestOutput.metrics 默认是 None
想测 TTFT/decode 速度,别指望 output.metrics(v1 默认不填)。自己掐表:
python
# max_tokens=1 测 TTFT;max_tokens=N 测整段;差值算 decode tok/s
ttft = time_of(generate(prompt, max_tokens=1))
t_full = time_of(generate(prompt, max_tokens=512))
decode_tps = 511 / (t_full - ttft)
还要先 warmup 一次摊掉首次 CUDA Graph 捕获;并 enable_prefix_caching=False,否则 warmup 同 prompt 会让 TTFT 测成缓存命中(实测 0.01s 假数据)。
坑 6:hf-mirror 限速
大批量下载会被限速,开 HF_HUB_ENABLE_HF_TRANSFER=1 + 多文件并行能缓解。
4. 核心 runner(精简版)
python
import os
os.environ["VLLM_USE_FLASHINFER_SAMPLER"] = "0"
for p in ("http_proxy","https_proxy","all_proxy","HTTP_PROXY","HTTPS_PROXY","ALL_PROXY"):
os.environ.pop(p, None)
from vllm import LLM, SamplingParams
llm = LLM(model="/mnt/sda/llm-bench/qwen3-30b-a3b-awq",
max_model_len=8192, gpu_memory_utilization=0.89,
enable_prefix_caching=False, enforce_eager=False) # dense 模型这里改 True
sp = SamplingParams(temperature=0.0, max_tokens=512, ignore_eos=True)
out = llm.generate(["请写一篇关于城市夜晚的散文"], sp)
完整脚本(含显存采样、批量吞吐、质量集、结果聚合、成本测算)见仓库 scripts/。
5. 成本:自建 vs API
按 4090 整机 0.5kW、0.7元/kWh、硬件 12000 元摊 3 年×8h/天:
| 模型 | 单用户/1M | 满并发/1M | 仅电费/1M |
|---|---|---|---|
| Qwen3-30B-A3B (MoE) | 2.45 元 | 0.25 元 | 0.05 元 |
对照 API(元/1M 输出):DeepSeek-V4-Flash 2.0、Qwen-Plus 8.5、Claude-Sonnet-4.6 106.5。
自建在并发跑满时 0.25 元/1M,比最便宜 API 低 6×、比 Claude-Sonnet 低 425×;但单用户场景升到 2.45 元/1M,与中端 API 持平。自建省钱的前提是并发利用率。 单用户自建的真正价值是数据私有 + 不计次调用 + 可微调。
6. 选型建议
- 综合首选:Qwen3-30B-A3B-Instruct-2507(最快、质量全能、Apache 2.0)。
- 代码助手 :Qwen3-Coder-30B-A3B(SQL 都知道写可走索引的范围查询而非
YEAR())。 - 离线重推理:DeepSeek-R1-Distill-32B(带思维链,质量最高,但慢)。
- 结论 :2026 年的 4090,别再跑 dense 32B 了,MoE 才是答案。
实测环境:RTX 4090 24G / 128G RAM / vLLM 0.23.0 / 2026-06。数据可复现,欢迎对拍。