1049 字
5 分钟
22GB 单卡推理实验记录
目的:在单张魔改 RTX 2080 Ti 22GB (SM75) 上验证 vLLM fork,积累可复现的 benchmark 数据, 为「单卡 TP=1 profile」开源贡献攒素材。所有数字均有日志出处,无推算混入。
这不是不同显卡之间的性能排名,而是一组固定硬件、固定模型和固定请求长度下的实验记录。重点是看 22GB 显存怎样在权重、KV 池和计算工作区之间分配,以及每次调整到底换来了什么。
阅读入口
- 概念解释:vLLM 推理部署概念速查
- 流程动画:vLLM 请求生命周期动画演示(浏览器打开,本数据集的数字都演了一遍)
环境
| 项 | 值 |
|---|---|
| 系统 | Windows 11 + WSL2 (Ubuntu 24.04.4) |
| GPU | Gigabyte RTX 2080 Ti 22GB(魔改),Turing / SM75,22528 MiB |
| 驱动 | 581.80(Windows 侧),WSL 映射,不装 Linux 驱动 |
| CUDA | 并存 13.3 / 12.8,构建显式用 12.8 |
| vLLM | 0.1.17(fork,基于上游 0.21.0),./.venv/bin/python |
| PyTorch | 2.11.0+cu128 |
| 模型 | QuantTrio/Qwen3.5-9B-AWQ(AWQ 4bit,LLM-only 加载权重 10.35 GiB) |
模型选型结论(单卡 22GB 三关筛选)
筛选条件:权重 ≤~17GB / 量化格式 fork 已验证 / 架构 fork 认识。
| 模型 | 权重 | 判定 |
|---|---|---|
| QuantTrio/Qwen3.5-9B-AWQ | 10.35 GiB(LLM-only) | ✅ 唯一全过,消融主实验台 |
| Vishva007/Qwen3.8-27B-W4A16-AutoRound | 17.2 GiB(LLM-only) | ✅ 意外跑通(4K 上下文是极限形态) |
| 一切 27B AWQ/INT4 | 19-22 GB | ❌ 物理墙(需双卡 TP=2) |
| google/gemma-4-31B-it-qat-w4a16-ct | 23.3 GB | ❌ 超上限 |
速度基线(端到端,256 tokens,含 prefill)
| 配置 | 耗时 | tok/s | 相对基线 |
|---|---|---|---|
| eager / seq=1 / 8K(原始基线) | 6.237s | 41.0 | — |
| + CUDA Graph | 5.553s | 46.1 | +12.5% |
| + MTP_K=2 | 3.19s | 80.3 | +96% |
| + MAX_BATCHED_TOKENS=2048 | 3.17s | 80.8 | 无改善(噪声内) |
- 最优配置:8K / GPU_UTIL 0.80 / CUDA Graph / prefix cache / MTP_K=2 / seq=1 / max_batched 1024 → 80.3 tok/s
- 理论 decode 上限 = 616 GB/s ÷ 12 GB 权重 ≈ 51 tok/s(eager 效率 ~80%)
- 27B 参考:23.9 tok/s(safe/4K/eager),理论上限 ≈ 32 tok/s,效率 ~75%
上下文 vs KV 池曲线(mamba 池之谜)
| MAX_MODEL_LEN | 可用 KV | 池子 tokens | 每 token 成本 | 超出地板价开销 |
|---|---|---|---|---|
| 8K | 7.06 GiB | 188,847 | ~39.2 KB | +1.3 GB |
| 32K | 7.06 GiB | 217,460 | ~34.0 KB | +0.37 GB |
| 128K | 7.06 GiB | 227,815 | ~32.5 KB | +0.05 GB |
| 262K | 9.26 GiB | 300,941 | ~32.3 KB | +0.01 GB |
- 地板价 ~32.3 KB/token = 16 层全注意力 × 2(K+V) × 4 heads × 128 dim × 2B(GDN 混合架构只有部分层是 full attention)
- 额外开销近似 ∝ 1/max_model_len:短上下文时 GDN state 池收一笔 ~1.3GB 的”过路费”,上下文越长摊得越薄
- 262K 实测:单卡 22GB + Qwen3.5-9B 原生 262K 上下文(GPU_UTIL 0.90),总显存 21998/22528 MiB,速度 38.6 tok/s(长上下文几乎免费,仅 -6%)
Prefix Cache
- 长前缀测试(~6.9K token 文档):冷请求 3.30s → 热请求 0.71s(4.6x),cached_tokens=6864
- 缓存块粒度 = 528 tokens(6864 = 528×13,等于 attention page 与 mamba page 的对齐块大小);短于一块的 prompt 无命中,非 bug
- 引擎 hit rate 实测 64.9%
MTP 推测解码
- QuantTrio/Qwen3.5-9B-AWQ 含 MTP 草稿头(config:
mtp_num_hidden_layers: 1) - MTP_K=2:Avg Draft acceptance 69.6%(per-position 0.785 / 0.607),Mean acceptance length 2.39/3.0
- 代价:KV 池 188,847 → 73,728 tokens(-61%,verify 工作区 + cudagraph capture size 3)
- MTP 与超长上下文天然冲突(262K + MTP 无余量);MTP 下 min_p / logit_bias 失效
环境排障记录(可复现,社区无文档)
- GPU_UTIL 启动检查差 90MB:0.95×22=20.9 > 可用 20.81 → 启动前体检直接拒绝;降到 0.94/0.93 通过
- 缺 g++-12:FlashQLA legacy GDN 内核 JIT 编译时
cc1plus: No such file or directory→sudo apt-get install -y g++-12(仓库把编译器 pin 死为 gcc-12,Ubuntu 24.04 默认只有 gcc-13 全家桶) - WSL 检测:
pin_memory=False警告,性能略降 - 推理期 Triton JIT 尖峰:首次请求触发 7 个 kernel 的 JIT 编译(
_zero_kv_blocks_kernel、_compute_slot_mapping_kernel、_copy_page_indices_kernel、_causal_conv1d_fwd/update_kernel、fused_recurrent_gated_delta_rule_packed_decode_kernel、_fused_post_conv_kernel),jit_monitor 建议扩展 warmup 覆盖
可能的后续贡献
- 单卡 TP=1 启动 profile:README 全部 profile 都是双卡 TP=2,单卡 22GB 验证无人做过 ← 主攻方向
- warmup JIT 缺口修复:fork 自带 jit_monitor 已提示方向,门槛最低
- GDN 短上下文 KV 池额外开销(~1.3GB @8K):机制待翻
kv_cache_utils.py确认,可开 issue - 单卡 AWQ 兼容表 / OOM 排查文档(g++-12 坑,WSL2 用户必踩)
- 上下文长度 vs KV 池曲线 benchmark
仓库背景:140+ issues、137+ PR,维护者天级响应,外部 PR 确有合并先例(kevinhirsch #133/#125/#111、hotwa #116、superniker #108);提交前须读 issue #74,重大贡献先在 issue/Discord 打招呼。
后续实验
- 32K→16K 中间点,验证 1/max_model_len 规律的中间形态
- MAX_NUM_SEQS=2~4 并发测试(KV 池 73K tokens 余量足够)
- MAX_BATCHED_TOKENS 在并发下的作用验证
- 27B @8K 边界实验(KV 111KB/token × 8K ≈ 0.9GB < 2.32GB 池,理论上装得下)
- 仓库只读扫描:profiles/ 目录、已关 issue,确认贡献点无人做过
- 数据整理成 hotwa #121 格式的 PR 草案
80.3 tok/s
MTP_K=2
262K
实测上下文
4.6x
Prefix Cache 热请求
系列文章
推理部署实验
相关文章
22GB 单卡推理实验记录
https://www.yyylegend.com/posts/22gb单卡推理实验记录/