7900XTX双卡TP,SGLang & VLLM 多Agent多并发测试对比(续二)- SGLang HiCache L3的最后一堵墙
-
双 7900 XTX + SGLang:我们把 Qwen Hybrid 的 L3 / NVMe Cache 推到了最后一道墙
过去的一周我一直在折腾HiCache L3在7900XTX里的实现问题:
能不能让 Qwen 这类 Hybrid 模型,把长期不用的上下文从显存 / 内存下沉到 NVMe SSD,需要时再恢复回来?也就是让HiCache的L3真正发挥作用。但是目前的状况是L3只写不读,功能失效。
我的目标其实很简单:
显存贵,DDR5 也越来越贵,SSD 相对便宜。
如果能把SSD拉入KV池的媒介列,然后把缓存做成:
VRAM → 小量 RAM 缓冲 → 大容量 NVMe
那么多 Agent、长上下文的本地推理成本会低很多。这意味着什么?随着AI模型的进步,模型给定的上下文可能越来越多,显存要求也越来越大。但是众所周知显卡和内存目前的价格有多离谱和疯狂,如果能用相对便宜的SSD提供替代解决方案,那么就给很多目前受限制的个人消费级PC提供了新的可能。
我个人觉得这个思路像“混合硬盘”:
快的介质做缓存,慢但便宜的大容量介质做仓库。
只是这里存的不是普通文件,而是模型的 KV Cache 和 MAMBA / Linear Attention 状态。
但是,目前卡在了SGLang的HiCache L3这一步。@enigma 大佬在他的含金量很高的帖子 https://lcz.me/topic/1620 里也碰到了和我一摸一样的问题,L3只写不读。
我花了大概一个星期时间,烧了几十亿Token(不算本地算力),感觉几乎走到了最后一步,虽然不成功,但是想记录下来,给感兴趣的人分享一下过程和思路。
一、我们的环境(我们=我和我的AI Agent)
测试平台:
- 2 × RX 7900 XTX 24GB
- ROCm 7.x
- SGLang
- Qwen Hybrid 模型
- TP=2
- GPTQ 4bit
- File backend 作为 L3 / NVMe cache
这个环境本身就有一点特殊。
SGLang upstream 后来逐步淘汰了旧 GPTQ 路径,原因也很好理解:
软件要往前走,就要不断简化旧代码、适配新的硬件和新的量化后端。
问题是,7900 XTX / RDNA3 恰好还依赖这条老 GPTQ 路线。
所以不是 RDNA3 kernel 不能跑,而是 upstream “轻装上阵”时,把这条 legacy 路径一起拿掉了。
最后我们确认:
底层
gptq_gemm_rdna3kernel 其实还在,只是 Python 注册、白名单和旧 GPTQ dispatch 被移除了。把这部分接回来以后,latest main 可以重新在 7900 XTX 上启动 GPTQ。
事实上,从某个以前的版本上找到适配模块,也确实嫁接成功了。
二、旧版本为什么 L3 一直跑不通
我们最早是在较老的 SGLang 版本上做 HiCache。
L1 / L2 都能正常工作:
GPU → RAM → GPU
MAMBA state 也可以正常写进 SSD。
问题出在真正的 L3 restore。
旧架构里:
Host cache node 被淘汰以后,会被直接从 radix tree 删除。
于是出现一个很尴尬的情况:
SSD 上的数据还在,
但是指向它的 node / metadata 已经没了。结果就是:
数据在 SSD 上,但新请求已经不知道该去哪里找它。
我们把这个问题称为:
L3 payload orphan
这也是旧路线最后的结构性 blocker。
三、latest main 出现了一个非常关键的新设计:buffer_only
后来我重新看 SGLang 最新 main,发现 upstream 已经换了思路。
新增的
buffer_only模式,本质上就是:RAM 不再承担完整 L2 Cache,
只做 GPU
SSD 之间的临时 staging buffer。也就是说,架构从:
GPU → 大 RAM Cache → SSD
变成更接近:
GPU → 小 RAM staging → SSD
这正好符合我们最开始的目标。
更重要的是:
它不再依赖 Host node 长期存活。
所以旧版本那个 “Host node 一删,SSD payload 变孤儿” 的问题,被新架构绕过去了。
四、MAMBA 居然真的走到了 L3 restore
这部分是这几天最大的突破。
我们已经实测证明:
- MAMBA 可以写进 L3
- storage key 正确
- L3 read 命中
- Host staging 正常
- MAMBA payload 可以 H2D
- device slot 可以 commit
- server 不再像旧版本那样直接崩
一度我们甚至拿到了完整的链路:
L3 key
→ Host staging slot
→ Device slot
→ MAMBA commit
→ Decode这已经说明:
MAMBA 并不是“完全不能做 L3”。
真正的问题已经缩到最后一层。
五、最后卡在哪里?
最后经过大量 trace,我们把问题压到了一个非常具体的位置:
Node Identity Split
同一个 request:
- prefix match 认为自己挂在旧 node,例如 node 19
- L3 restore 后,MAMBA payload 被 materialize 到一个新 node,例如 node 32
- payload 在 device slot 27
- 但 decode 最后使用的是另一个 slot,例如 slot 28
也就是说:
数据恢复成功了,但 request 仍然握着旧的“身份”。
恢复后的新 node 和 request 当前 anchor 没有重新对齐。
简单说就是:
仓库把货送回来了,
但系统把货放到了新货架,
而取货单还指向旧货架。这已经不是简单的 memcpy、slot clear 或同步问题了。
它开始涉及:
- radix node identity
- request re-anchor
- scheduler admission
- MAMBA CoW / handoff
也就是从“小修 wiring”进入了架构语义层。
六、所以最后我们选择停手
我们一开始给自己定了一个原则:
优先找最短路径。
如果只是几个局部 wiring bug,就修。
如果开始进入 scheduler / radix tree / request lifecycle 的重构,就停。现在正好到了这个边界。
所以最终结论不是:
“失败了。”
而是:
我们已经证明 latest main 的 buffer_only 架构,确实可以把 Hybrid 模型的 MAMBA state 推到 NVMe L3 restore 的最后一公里。
真正还缺的是:
L3 restore 后,request-side node identity 如何重新和 materialized node 对齐。
七、这件事为什么值得继续关注
我觉得这个方向对本地 AI 很有价值。
未来模型上下文从 256K 走到 512K、1M,并不奇怪。
但显存和大容量 DDR5 都很贵。
如果 cache hierarchy 能真正变成:
VRAM
↓
小量 RAM staging
↓
大容量 NVMe那么多 Agent 长期共存会现实很多。
尤其是 4 卡、8 卡、本地工作站这类机器:
计算能力可能够,
真正限制多 Agent 的反而会变成上下文驻留成本。所以我很看好 L3 / NVMe cache 这条路线。
当前最终状态
已经验证:
- 双 7900 XTX + latest SGLang main
- GPTQ RDNA3 路径恢复
- Hybrid FULL + MAMBA
- buffer_only
- MAMBA L3 write
- MAMBA L3 read
- Host staging
- H2D
- device commit
- 双请求 ownership
- abort / cleanup
- 85 分钟 soak
- 无资源泄漏
最后剩余 blocker:
L3 restore 后的 node re-anchor / identity handoff
所以暂时收工。
如果 upstream 后续补上 MAMBA buffer_only 的 re-anchor / state handoff,这套东西很可能就真的能完整闭环。
届时我会继续回来测试。
即使L3不工作,但是在上一篇帖子里我介绍了, 我目前7900XTX双卡在SGLang+HiCache L1/L2 上跑Hermes双Agent 256K 上下文非常流畅丝滑,几乎不输云端模型体验。具体技术细节,推荐大家参考 https://lcz.me/topic/1620 里面干货满满。
-
好帖,把「L3 payload orphan」和「node identity split」这两层分得很清楚。补几个可验证的方向,供你或后续接手的人参考:
- Node Identity Split 的本质多半不是状态没恢复,而是 request 的锚点没重绑。restore 之后 prefix match 命中的是旧 node(19),materialize 生成了新 node(32),但 req 的 last_node / prefix_indices 仍指向 19。修法通常是 restore 完成后强制重跑一次 match_prefix,用返回的 node 覆盖 req 当前锚点,而不是只在 pool 层把 payload 搬对。
- 第二个错位是 MAMBA slot:payload 恢复到了 device slot 27,decode 却用 slot 28。要查 commit 之后谁在改 slot——是 scheduler admission 复用了 slot,还是 CoW/handoff 又分配了一个。建议在 commit 处打一条 (req_id, node_id, pool_idx) 三元组日志,decode 前再打一次,直接抓是哪一步把 idx 换掉的。
- 你停手的边界判断是对的:一旦动到请求生命周期,就不该在 fork 里硬修。建议把这份 trace 直接提 upstream issue,标题点明「buffer_only + MAMBA L3 restore: request node/slot identity not re-anchored」,附 19/32、27/28 这组最小复现,比继续改分支更快让上游接手。
- 一个不碰架构的临时绕法:L3 restore 命中后,让该 request 只认新 node——放弃旧锚点,在 restore 完成点重建 prefix 绑定;语义上允许的话,这是能用 L1/L2 兜住的过渡方案。
- GPTQ on RDNA3 那段很有价值。上游砍 legacy path 是趋势,gptq_gemm_rdna3 kernel 还在、只是注册/dispatch 被删——长期要么以插件形式把 dispatch 挂回去,要么尽快迁到 AWQ/marlin 这类还在维护的后端,别让整条链锁死在旧 GPTQ。
最后那段结论我认同:这不是失败,是把问题收窄到了最后一道语义墙。L1/L2 上双 Agent 256K 流畅跑这件事本身已经很有说服力。
-
,
T terry 固定了此主题
-
补充参考资料出处:
SGLang 官方把 HiCache 分成:
L1 = GPU
L2 = Host RAM
L3 = Storage热数据留在显存,暂时不用的上下文往下沉,需要时再拉回来。
VRAM → 少量 RAM staging → NVMe / Storage
这其实正是 SGLang HiCache 本身正在做的事情。
而 latest main 里的
buffer_only又更进一步(开发完善中):Host RAM 不一定要承担一个很大的完整 L2 Cache,也可以主要作为 GPU
Storage 之间的 staging buffer。另外更有意思的是,SGLang 官方现在已经把 Agent 场景下的 Distributed KV Cache 列进 Roadmap,而且明确提到:
Agentic workload 会快速增加 KV Cache 的存储和传输压力,同时 HiCache 对 Hybrid models 的兼容性目前仍然有限。
官方 Roadmap:
https://github.com/sgl-project/sglang/issues/21846
还有一份 HiCache storage framework 的 Roadmap:
https://github.com/sgl-project/sglang/issues/18239
另外,vLLM 也已经在推进类似的 Tiered KV Offloading:
GPU → CPU Primary Tier → Secondary Storage
所以“GPU + RAM + Storage”的分层缓存,并不是某一个框架的特殊玩法,而是现在推理框架都在逐渐探索的方向。
vLLM 官方资料:
https://docs.vllm.ai/en/latest/features/kv_offloading_usage/
https://docs.vllm.ai/en/latest/api/vllm/v1/kv_offload/tiering/spec/
-
,系统 取消固定了此主题

