📚 双 RTX 5060 Ti 部署 Qwen3.8 系列 · 5/6
💡 导读:
长上下文显存最容易算错的地方,是把 Hybrid Attention 模型直接当成传统 64 层 Full Attention。Qwen3.8-27B 实际只有 16 层传统 Full Attention,这会把 200K 的 FP8 KV 理论值从约 12.2 GiB/GPU 改写为约 3.05 GiB/GPU,但真实生产边界仍然要看完整 Runtime。
前面已经把模型权重和 GPU 执行链基本处理清楚了。
接下来真正开始和长上下文抢显存的,就是 Cache。
但这里有一个非常容易算错的问题。
如果把 Qwen3.8-27B 当成一个传统的 64 层 Full Attention 模型,那么在 TP2 + FP8 KV 下,200K Context 会算出大约:
12.2 GiB / GPU
对于一张 16GB 的 RTX 5060 Ti,这几乎意味着根本没有正常部署空间。
但 Qwen3.8-27B 实际不是这种结构。
它采用混合 Attention:
48 × Gated DeltaNet + 16 × Full Attention
真正随着 Context 长度线性增长的传统 KV Cache,主要来自其中 16 个 Full Attention Layer。
重新计算以后:
100K ≈ 1.53 GiB / GPU200K ≈ 3.05 GiB / GPU
差了整整四倍。
不过这仍然不意味着我的双 5060 Ti 最终能稳定跑 200K。
实际最终采用的是:
2 × RTX 5060 Ti 16GB
Qwen3.8-27B NVFP4
TP2
FP8 KV
CUDA Graph ON
gpu_memory_utilization = 0.97
max-model-len = 100K
vLLM Profile 最终显示:
Maximum Concurrency ≈ 1.07x
这篇真正想回答的就是两个看起来有点矛盾的问题:
为什么 200K 的传统 KV Payload 理论上只有约 3.05GiB / GPU,但最终生产配置却停在了 100K?
以及:
为什么我最后选择 FP8 KV,而没有继续把 KV 压到 INT4?
1. 先把 Qwen3.8 的 KV Cache 算对
自回归生成时,前面 Token 已经算出来的 Key 和 Value 不需要在每一步重新生成,因此会被缓存下来。
概念上可以理解成:
Token 1 → K1 / V1
Token 2 → K2 / V2
Token 3 → K3 / V3
...
对于一层传统 Full Attention,每个 Token 的 KV Cache 可以近似写成:
KV Heads
× Head Dim
× 2(K + V)
× dtype bytes
整个模型则是:
Tokens
× Full Attention Layers
× KV Heads
× Head Dim
× 2
× dtype bytes
因此对纯 Full Attention 模型来说,Context 翻倍,KV Cache 基本也会跟着翻倍。
但这个公式有两个很重要的前提:
- 模型到底有多少层真正使用 Full Attention;
- 每层到底需要缓存多少个 KV Head。
Qwen3.8-27B 在这两个地方都不能按“传统 64 层 Transformer”直接套公式。
GQA 先把 KV Head 数降了下来
Qwen3.8-27B 的 Full Attention 部分大致是:
Query Heads = 24KV Heads = 4Head Dim = 256
虽然 Query 有 24 个 Head,但真正需要长期缓存的 K/V 只有 4 组。
传统 MHA 更接近:
24 Q / 24 K / 24 V
GQA 则是:
24 Q
↓ 共享
4 K
4 V
所以算 KV Cache 时,真正需要代入的是:
KV Heads = 4
而不是 24。
这也是 GQA 对长 Context 显存非常直接的价值。
如果错误地把 64 层全部当成 Full Attention
先故意按传统模型算一次。
条件:
64 Layers
KV Heads = 4
Head Dim = 256
FP8 KV
TP2
TP2 下,4 个 KV Head 分到两张 GPU,每张大致保存 2 个。
每层、每 Token、每 GPU:
2 KV Heads
× 256
× 2(K + V)
× 1 Byte
= 1024 Bytes
64 层:
1024 × 64 = 64 KiB / Token / GPU
200K Context:
64 KiB × 200,000 ≈ 12.2 GiB / GPU
如果 Qwen3.8-27B 真的是 64 层全部 Full Attention,单是 200K 的 FP8 KV 就接近吃掉一张 16GB 卡的大部分显存。
这时候再讨论 23GB 权重、CUDA Graph、Runtime,基本就没有意义了。
问题在于:
Qwen3.8-27B 不是这种结构。
Qwen3.8-27B 只有 16 层传统 Full Attention
Qwen3.8-27B 的 64 层按照下面的模式循环:
3 × Gated DeltaNet + 1 × Full Attention
一共循环 16 次。
最终就是:
48 × Gated DeltaNet16 × Full Attention
于是传统 KV 重新计算:
2 KV Heads / GPU
× 256
× 2(K + V)
× 1 Byte
× 16 Layers
=
16 KiB / Token / GPU
对应不同 Context:
| Context | FP8 Full-Attention KV / GPU |
|---|---|
| 32K | ≈0.49 GiB |
| 64K | ≈0.98 GiB |
| 100K | ≈1.53 GiB |
| 128K | ≈1.95 GiB |
| 200K | ≈3.05 GiB |
| 256K | ≈4.00 GiB |
所以同样是 200K:
错误按 64 层 Full Attention:
≈ 12.2 GiB / GPU
按实际 16 层 Full Attention:
≈ 3.05 GiB / GPU
正好差了接近四倍。
这也是为什么面对 Hybrid Attention 模型时,不能只知道“它有 64 层”,就直接套传统 KV 公式。
另外 48 层并不是完全没有状态
Gated DeltaNet 不需要像 Full Attention 一样,为每个历史 Token 保存完整 K/V。
它更接近维护 recurrent state:
历史信息
↓
不断更新 State
↓
当前 State
Full Attention 的传统 KV 是:
Context 越长 → KV 越大
而 Gated DeltaNet 的 recurrent state 本身不是这种“每多一颗历史 Token 就追加一份完整 K/V”的线性增长方式。
因此 Qwen3.8-27B 的 Cache 更准确地理解为:
16 × Full Attention
→ 随 Context 增长的传统 KV Cache
48 × Gated DeltaNet
→ per-sequence recurrent state
这解释了为什么它在超长 Context 下,比“64 层全部 Full Attention”的模型省很多传统 KV。
但同时也提醒我们:
3.05GiB 只是 Full Attention KV Payload,不是整个 Engine 最终的显存成本。
2. 1.53GiB 理论 KV,不等于只多占 1.53GiB 显存
100K FP8 Full-Attention KV 的理论值只有:
≈ 1.53 GiB / GPU
但 GPU 上同时还需要存在:
- NVFP4 Weights
- Gated DeltaNet State
- CUDA Runtime
- CUDA Graph
- Peak Activation
- Workspace
- Allocator
- KV Block / Metadata
- 通信 Buffer
- 框架其他 Buffer
所以实际绝对不能直接做:
16GB
-
权重
-
1.53GB
=
剩余显存
理论公式真正回答的是:
Context 每增加一段,传统 Full Attention KV Payload 大概增长多少?
而真实部署要回答的是:
模型加载、Graph、Activation、State 和所有 Buffer 都存在以后,到底还能留多少 Cache Capacity?
前者靠公式。
后者靠实际 Profile。
3. 从理论 Payload 到真实 Runtime 边界
最终跑通并采用的配置是:
GPU:
2 × RTX 5060 Ti 16GB
Model:
Qwen3.8-27B NVFP4
Parallel:
TP2
KV Cache:
FP8
CUDA Graph:
ON
gpu_memory_utilization:
0.97
max-model-len:
100K
vLLM Profile 给出的:
Maximum Concurrency ≈ 1.07x
更准确的理解不是简单说:
100K × 1.07 = 107K
然后把 107K 当成一个精确的“总 Cache Token 数”。
它更适合被理解为:
在当前
max-model-len = 100K和当前 Cache 配置下,Profile 估算只能同时容纳大约 1.07 条达到最大长度的 Sequence。
也就是说,这套配置对:
1 × 100K 长上下文
已经比较接近当前 Profile 给出的容量边界,余量并不算大。
这里还要再加一层保险:Qwen3.8-27B 属于 Full Attention + Gated DeltaNet 的 Hybrid Model,vLLM 的 Maximum Concurrency 是当前版本 Cache Manager 根据实际 Cache 配置给出的 Profile 指标,不应该被当成一个脱离版本、Backend 和 Hybrid Cache 实现后仍然精确成立的物理常数。
所以我把:
Maximum Concurrency ≈ 1.07x
理解成:
当前这套 Runtime 配置已经非常接近 1 条最大长度 Sequence 的容量边界。
而不是拿它反推出一个精确的“总 Cache Token 数”。
所以:
200K 传统 KV Payload ≈ 3.05 GiB / GPU
并不能直接推出:
200K 一定能稳定启动并保留 CUDA Graph 等运行配置
决定最终 Context 上限的是整套 Runtime 显存,而不是传统 KV 这一项。
max-model-len 不是“每个请求固定预留这么多 Cache”
设置:
--max-model-len 100000
表示的是:
单条 Sequence 最大允许达到 100K。
它不等于“每来一个请求,就静态给它预留完整 100K KV”。
vLLM 实际维护的是共享 Cache Pool。
概念上,如果当前几个请求分别占用:
请求 A:40K
请求 B:30K
请求 C:20K
请求 D:10K
它们会共同消耗 Engine 的 Cache Capacity。
而不是:
4 × 100K = 400K
全部提前保留。
所以生产 Serving 不能只问:
“我设置 100K,可以并发几条?”
还要看实际请求长度分布、Block 使用情况以及 Hybrid State 的 per-sequence 成本。
Hybrid Model 下,总 Token 相同也不代表显存完全相同
只考虑 Full Attention KV:
1 × 100K
和:
5 × 20K
总 Token 数一样,传统 KV Payload 也接近。
但 Qwen3.8-27B 还有 per-sequence recurrent state。
一条 100K Sequence 只需要一套 Sequence State。
五条 20K Sequence 则需要五套。
除此之外还有:
- Scheduler State
- Output Buffer
- Block Fragmentation
- 其他 per-request allocation
所以在 Hybrid Model 下:
总 Token 数相同,不代表最终显存占用完全相同。
这也是为什么 Maximum Concurrency 和实际并发能力,最终还得通过真实 workload 测。
4. KV 精度怎么选:BF16、FP8 与 INT4
对于这 16 个 Full Attention Layer,TP2 下:
100K
BF16 KV
≈ 3.05 GiB / GPU
FP8 KV
≈ 1.53 GiB / GPU
200K
BF16 KV
≈ 6.10 GiB / GPU
FP8 KV
≈ 3.05 GiB / GPU
对一张只有 16GB 的 5060 Ti 来说,每卡节省 1.5GB 或 3GB 已经足以直接改变最终可用 Context。
所以我的实际组合是:
Weights:NVFP4KV:FP8
权重和 KV 根本没有必要追求同一种 bit 数。
它们走的是不同的数据路径,也有不同的性能瓶颈。
INT4 KV 确实更省,但不能只看 bit 数
只从数据量来看:
FP8 = 1 Byte / elementINT4 ≈ 0.5 Byte / element
所以理论上传统 KV Payload 还能再减半。
例如 100K:
FP8 ≈ 1.53 GiB / GPUINT4 ≈ 0.76 GiB / GPU
200K:
FP8 ≈ 3.05 GiB / GPUINT4 ≈ 1.53 GiB / GPU
容量收益确实很诱人。
但 KV Cache 和“硬盘压缩”不一样。
Attention 每一轮都要频繁读取这些数据。
INT4 KV 往往还会涉及:
packed INT4 read
↓
unpack
↓
scale / dequant
↓
格式转换
↓
Attention
所以:
Memory Traffic ↓
并不自动等于:
Latency ↓
额外 unpack、scale 和反量化本身也要花算力与 Kernel 开销。
为什么这套 5060 Ti 最后还是选 FP8 KV?
RTX 5060 Ti 属于 Blackwell。
在这套硬件上,我更关心的是:
当前框架的 FP8 Attention / Cache 路径能不能稳定、高效地运行。
而不是单纯追求最低的 KV bit 数。
INT4 KV 的优势很明确:
- 更省显存
- 更大的理论 Context
但代价也同样明确:
- packed read / unpack
- dequant / scale
- Kernel 路径更复杂
- 可能增加 Decode 计算开销
- 精度风险也更激进
FP8 KV 虽然占用比 INT4 大一倍,但在当前硬件和推理框架里,是一个更自然的平衡点:
- 容量已经明显低于 BF16
- Kernel 路径更成熟
- 不需要为了进一步压缩增加过多低 bit 解码工作
- 精度选择也更保守
更重要的是,这套机器使用 FP8 KV 已经可以做到:
100K Context + Maximum Concurrency ≈ 1.07x
对我的真实代码任务来说,这已经够用了。
所以没有必要为了“Context 数字更大”而继续把 KV 压到 INT4,最后反而让 Decode 更慢。
为什么是 NVFP4 权重 + FP8 KV?
表面上看:
Weights:4bitKV:8bit
好像精度“不统一”。
但实际非常合理。
NVFP4 权重
主要目标是:
- 让 27B 权重完整 GPU Resident
- 减少 Weight Traffic
- 利用 Blackwell 对 NVFP4 的低精度 GEMM 路径
FP8 KV
主要目标是:
- 降低长 Context 显存
- 保持更直接的 Attention 执行路径
- 避免更低 bit 带来的额外 unpack / dequant 成本
- 保持长上下文稳定性
所以实际部署并不是:
“整个模型统一选一个 bit 数。”
更合理的思路是:
权重、KV、Activation 分别选择当前 GPU 和当前 Kernel 最合适的数据格式。
这也是低精度推理真正进入工程阶段以后,和“模型量化成几 bit”这种单一描述最大的区别之一。
5. 最终配置
最后我实际采用的是:
2 × RTX 5060 Ti 16GB
Qwen3.8-27B NVFP4
TP2
FP8 KV
CUDA Graph ON
gpu_memory_utilization = 0.97
max-model-len = 100K
结果:
Maximum Concurrency ≈ 1.07x
对这套硬件来说,它形成了一个比较合理的平衡:
27B 模型质量
+
权重完整 GPU Resident
+
TP2 Decode 性能
+
100K Context
+
FP8 KV
+
CUDA Graph
而不是为了追求一个单独的:
200K
把 KV 精度、Decode 性能和显存稳定性同时推到极限。
6. 结论
Qwen3.8-27B 的长 Context 显存不能按照:
64 Layers × 传统 KV Cache
直接计算。
它实际是:
48 × Gated DeltaNet + 16 × Full Attention
再加上:
24 Query Heads / 4 KV Heads
的 GQA。
因此 TP2 + FP8 KV 下,仅计算传统 Full-Attention KV Payload:
100K
≈ 1.53 GiB / GPU
200K
≈ 3.05 GiB / GPU
但真实运行还必须容纳:
- Weights
- Gated DeltaNet State
- Runtime
- Activation
- CUDA Graph
- Workspace
- Allocator
- 其他 Buffer
所以最终实际采用的是:
100K Context
FP8 KV
gpu_memory_utilization = 0.97
Maximum Concurrency ≈ 1.07x
这给出了一个我现在很喜欢的判断方式:
公式负责解释显存模型,vLLM Profile 负责找边界,真实长上下文压测负责定生产配置。
而 NVFP4 Weights + FP8 KV 也不是精度选择混乱。
它只是分别给不同数据路径选择更合适的格式。
到这里,“100K 能不能装下”的问题基本结束了。
接下来的问题变成:
100K Context 真正跑起来以后,怎么避免每一轮 Agent 都重复计算几十 K、甚至接近 100K 的相同 Prompt?
下一篇进入整个系列最后一层:
《长上下文推理优化:Prefix Cache、调度与 MTP》
系列导航: ← 上一篇:为什么生产推理我默认 vLLM:从 NVFP4、Kernel 到 CUDA Graph · 下一篇:长上下文推理优化:Prefix Cache、调度与 MTP →