3-bit 量化模型的诱惑与真相:我在双 3090 上实测了 GSQ 3bit + vLLM
上个月我写了一篇关于 SGLang 服务 Qwen3.8-27B 的文章,把 4-bit 量化模型在单张 3090 上的上下文从 65K 一路优化到了 102K。这个月,一个”更狠”的东西出现了——3-bit 量化模型。权重比 4-bit 再小一截,理论上能装下三倍的上下文。听起来完美,但真的能用于生产吗?
我用周末时间,在双 RTX 3090 工作站的空闲卡上,把 GSQ 3bit 模型 + vLLM 推理引擎完整实测了一遍。过程跌宕起伏:有死机、有版本号陷阱、有”看似优化实则倒退”,但最终拿到了清晰的答案。
一、vLLM 版本:别被版本号骗了
这次测试要用 vLLM,因为 GSQ 3bit 是 vLLM 路线(SGLang 只支持 4/8-bit 量化)。我从 Docker Hub 拉取了 vllm/vllm-openai:cu129-nightly,结果启动后版本号显示 0.26.1rc1.dev1102。
查遍 GitHub,最新正式版明明是 v0.27.1(8 月 11 日发布)。一个”0.26.1″的滚动版本,居然能加载最新代码才能支持的 3-bit 模型?
这里有一个很容易踩的坑:vLLM 的 nightly 镜像版本号是用 git describe 从最近的分支点派生的。0.26.1rc1.dev1102 的意思是”挂在 0.26.1-rc1 标签之后、滚动了 1102 个提交”——它不但不是旧版,反而是比 0.27.1 更新 11 天的滚动最新版。3-bit 支持在官方 PR 还是 open 状态时,nightly 已经悄悄合入了。
结论:判断镜像新旧别看版本号,看构建日期。nightly 08-22 构建 > release 08-11。
二、GSQ 3bit:数据漂亮得不像量化模型
GSQ(Generalized Smooth Quantization)是 ISTA-DASLab 的量化方案。Qwen3.8-27B 的 3-bit 版本有两个亮眼数据:
AIME 2024 数学竞赛:100 分无损(对 3-bit 来说几乎是奇迹)
GPQA 科学推理:91.41,反超 BF16 原版
更关键的是校准集:作者在标准校准数据之外,混入了 10% 的 agentic 场景数据(工具调用、多步推理)。这正是 coding agent 的分布。
模型文件 13GB,--language-model-only 纯文本模式下权重只占 10.6GB。在 24GB 显存上,这意味着 KV cache 池理论上有巨大空间——同一张卡,4-bit 只能塞 10 万 token 上下文,3-bit 可能冲到 30 万。
三、部署:一次死机换来的三件套
测试从部署就开始踩坑。vLLM 镜像的 entrypoint 是 vllm serve,直接 docker run 传自定义命令会被当成 model 参数吞掉;容器里的 Python 命令是 python3 不是 python。这些是小问题。
大问题是死机。vLLM V1 引擎启动时会用 torch.compile 编译全部算子,编译期 CPU 和内存双满载。我第一次带 CUDA graph 启动时,叠加 GPU1 上 SGLang 常驻的 14GB,主机直接假死,重启电脑。
之后我形成了一套”防死机三件套”:
一、测试阶段用 --enforce-eager 跳过 torch.compile(代价是慢)
二、等确认环境稳定后,改用 --cudagraph-capture-sizes 1 2 3 限制 CUDA graph 捕获的 batch 规模——这是最关键的参数,vLLM 默认会为 1 到 512 的每个 batch 规模捕获一份 graph,白白吃掉大量显存。限制到 1/2/3(匹配 3 并发),graph 内存从好几 GB 降到 0.45GB
三、--max-num-seqs 3 限制并发(类比 SGLang 的 3 并发),进一步压缩 graph 需求
还有一个隐藏的 OOM 陷阱:当 KV cache 分配报”需要 12.25GB”时,你以为换更小的 max-model-len 能解决,结果 65K 和 131K 报同样的错。因为这个 12.25GB 是 CUDA graph 的内存估计,跟上下文长度无关。只有限制 capture sizes 才是正解。
四、速度优化:四段旅程,找到真正的杠杆
部署跑通只是第一步,测速结果让我怀疑人生:eager 模式下 medium 场景只有 15.7 t/s。GSQ 官方自承”2-bit 和 3-bit 的 kernel 效率不如 4-bit 和 8-bit”,难道 3-bit 真的慢?
我用控制变量法一路挖下去:
第一站,CUDA graph。 开启后 medium 从 15.7 飙升到 39 t/s,提速 2.5 倍。但 long(30K 上下文)只有 8.2 t/s,短场景则只有 34——一个不合理的数字,比 medium 还慢。
这里我犯了个测量错误:短场景输出只有 9 个 token,固定开销(TTFT、调度)摊在极少 token 上,t/s 被严重低估。把输出拉到 500 token 再测,short 的真实速度是 50.6 t/s。测量纪律很重要:测速至少要有几百个输出 token,否则全是噪声。
第二站,bf16 KV 替换 fp8 KV。 这是全篇最大的发现。3090 是 Ampere 架构,没有 FP8 tensor core。fp8 的 KV cache 虽然省显存,但每次读取都要反量化,长上下文的 KV 量巨大,这个开销被线性放大。换成 bf16 KV 后,30K 上下文从 8.2 提升到 15.9 t/s,几乎翻倍,而 medium 场景 45.9 t/s 完全不受影响。bf16 KV 只牺牲了一点显存(KV 池从 29 万 token 降到 16 万),但 100K 单请求依然装得下。
第三站,换 attention backend。 vLLM V1 支持 flash-attn、flashinfer、triton 等多个后端。SGLang 用 flashinfer 在 3090 上表现极好,我满怀期待地切过去——结果长上下文反而从 8.2 掉到 5.8 t/s。这条路是死胡同,直接排除。
第四站,回归本质。 把数据摆在一起看:medium 46 t/s、30K 16 t/s、70K 3.1 t/s。长上下文的衰减呈线性,这不是任何参数能救的——O(n) 注意力是物理上限。每次生成一个 token,都要重新注意力到全部历史 KV,上下文越长,单 token 成本越高,3090 单卡无法避免。
五、一个重要的方法论结论
很多人看到”3-bit kernel 效率低”就归咎于量化方案。但我的实测给出了更精确的答案:
3-bit kernel 不是长上下文慢的原因。 权重反量化是固定开销——每次前向都要反量化整个模型,与上下文长度无关。如果 3-bit kernel 真的拖后腿,那么 short 和 medium 场景也应该慢。但实测 short 50.6、medium 45.9,完全对齐甚至反超 4-bit 的 SGLang。
长上下文的瓶颈在 attention 和 KV 读取,这是框架层的物理限制,与量化位宽无关。这个区分很重要:它意味着”换更高效的 3-bit kernel”不会拯救长上下文性能。
六、结论:数据虽美,不为所用
最终判决是不切换生产。理由很直白:
中短上下文 46 t/s,和现有的 RedHatAI 4-bit(SGLang)持平,没有速度优势
长上下文 70K 只有 3.1 t/s,对 coding agent 的多轮长对话完全不可用
为了省显存换三倍上下文,代价是慢一个数量级,不划算
还要维护第二套推理引擎 + 补丁 + 死机风险
但这次实验并非一无所获,沉淀下来的是几个能长期复用的方法论:
一、Ampere 上 bf16 KV 优于 fp8:省显存的 fp8 在长上下文反而更慢,这个教训适用于任何 vLLM 部署
二、CUDA graph 捕获规模必须显式限制:默认捕获到 512 是显存黑洞
三、速度测量必须有足够长的输出:小输出测出的 t/s 全是噪声
四、长上下文性能是物理上限:评估模型时,中短速度和长上下文衰减要分开看,别用短场景的成绩掩盖长场景的短板
技术选型就像买跑车:马力(量化密度)只是纸面参数,真正决定体验的是你在什么路况(上下文长度)下开。GSQ 3-bit 在短途是辆好车,但我们要跑的是长途。
本文基于 2026-08-23 在双 RTX 3090 工作站的完整实测。完整实验数据与留痕见 docs/review/gsq-3bit-vllm-verdict-2026-08-23.md 与 docs/review/vllm-deployment-lessons-2026-08-23.md。
