执行摘要
- 一句话:为 Mooncake 等后端添加 draft KV 卸载支持
- 推荐动作:该 PR 实现了关键的 draft KV 卸载功能,设计较稳健,建议精读 cache_controller.py 中的 _maybe_register_draft_with_storage 和 _draft_page_set_v2 函数以理解零拷贝集成模式。测试重构思路也值得参考。
功能与动机
关联 Issue #24956 报告在使用 GLM5 / DeepSeek V3.2 + HiCache + MTP 时触发 assert target_locations is not None 崩溃,根本原因在于 draft KV 无法正确卸载到 Mooncake 存储。PR 旨在通过支持 draft KV 卸载来修复该问题,并在重启后保持推测解码性能。
实现拆解
实现步骤
- 引入 draft I/O 函数指针:在 CacheController.init 中添加 draft_page_get_func 和 draft_page_set_func,初始均为 None。
- 实现专用 I/O 路径:新增 _draft_page_set_v2 / _draft_page_get_v2(零拷贝多池路径)和 _draft_page_set_generic / _draft_page_get_generic(通用路径)。
- 条件注册:_maybe_register_draft_with_storage 根据 storage_backend_type 选择路径:mooncake 注册 v2 并要求 should_split_heads 为 False;hf3fs 等暂不支持,走通用路径。
- 存储后端适配:mooncake_store.py 中扩展 register_mem_host_pool_v2 以处理 PoolName.DRAFT,并修改 _get_hybrid_page_component_keys 按 draft 池的 MLA/MHA 类型生成独立后缀。
- 枚举扩展:hicache_storage.py 新增 PoolName.DRAFT,确保键空间隔离。
- 测试重构:提取 HiCacheSpecStorageMixin 公共基类,File 和 Mooncake 测试继承它,大幅减少复制代码。
- 配套调整:scheduler.py 中跳过 HybridLinearKVPool 的 draft 注册(暂不支持)。
关键文件:
python/sglang/srt/managers/cache_controller.py(模块 缓存控制;类别 source;类型 core-logic;符号 _maybe_register_draft_with_storage, _draft_page_set_v2, _draft_page_get_v2, _draft_page_set_generic): 核心变更文件,新增 draft I/O 函数指针、v2 零拷贝路径及后端选择逻辑。
python/sglang/srt/mem_cache/storage/mooncake_store/mooncake_store.py(模块 存储后端;类别 source;类型 dependency-wiring;符号 register_mem_host_pool_v2, _get_hybrid_page_component_keys): 修改了月亮蛋糕存储后端的 pool 注册和键生成,以支持 draft 池。
python/sglang/srt/mem_cache/hicache_storage.py(模块 存储定义;类别 source;类型 data-contract;符号 PoolName): 新增 PoolName.DRAFT 枚举。
python/sglang/test/hicache_spec_storage_common.py(模块 测试公用;类别 test;类型 test-coverage;符号 HiCacheSpecStorageMixin, _get_spec_server_args, _launch_spec_server, _restart_spec_server): 新增公共测试基类,消除了两个测试文件的大量重复。
test/registered/hicache/test_hicache_spec_mooncake_storage.py(模块 Mooncake测试;类别 test;类型 test-coverage;符号 TestHiCacheSpecMooncakeStorage, setUpClass, tearDownClass, _start_mooncake_store_service): 新的 Mooncake 端点集成测试。
test/registered/hicache/test_hicache_spec_file_storage.py(模块 文件存储测试;类别 test;类型 test-coverage;符号 TestHiCacheSpecFileStorage, _get_spec_server_env, _count_file_storage_pages, _wait_for_file_storage_pages): 重构为使用公共基类,大幅缩减代码。
关键符号:_maybe_register_draft_with_storage, _draft_page_set_v2, _draft_page_get_v2, _draft_page_set_generic, _draft_page_get_generic, register_mem_host_pool_v2, _get_hybrid_page_component_keys, HiCacheSpecStorageMixin._get_spec_server_args, HiCacheSpecStorageMixin._launch_spec_server
关键源码片段
python/sglang/srt/managers/cache_controller.py
核心变更文件,新增 draft I/O 函数指针、v2 零拷贝路径及后端选择逻辑。
def _maybe_register_draft_with_storage(self) -> None:
"""Wire draft host pool to the storage backend.
# 根据后端类型选择 draft I/O 实现路径
# - mooncake: 使用 v2 零拷贝多池路径(需注册 draft 主机池)
# - hf3fs / eic / nixl / simm: 尚不支持,跳过
# - 其他后端: 使用通用路径(_draft_page_get/set_generic)
"""
# 先重置函数指针,确保后续调用不会误用旧路径
self.draft_page_get_func = None
self.draft_page_set_func = None
# 只有同时启用了 draft 和 storage 才进行注册
if not self.has_draft or not self.enable_storage:
return
backend = self.storage_backend_type
# ---- mooncake 零拷贝 v2 路径 ----
if backend == "mooncake":
# should_split_heads 下 suffix 生成尚不兼容,暂时禁用
if self.storage_config.should_split_heads:
logger.warning(
"HiCache draft L3 disabled: should_split_heads not yet "
"supported on the mooncake v2 path."
)
return
# 注册 draft 主机池到 mooncake store,使用 PoolName.DRAFT 区分键前缀
self.storage_backend.register_mem_host_pool_v2(
self.mem_pool_host_draft, PoolName.DRAFT
)
self.draft_page_get_func = self._draft_page_get_v2
self.draft_page_set_func = self._draft_page_set_v2
return
# ---- 其他零拷贝后端暂不支持 ----
if backend in {"hf3fs", "eic", "nixl", "simm"}:
logger.warning(
"HiCache draft L3 disabled: backend %s does not yet support "
"draft pool registration.",
backend,
)
return
# ---- 通用后端路径(非零拷贝) ----
self.draft_page_get_func = self._draft_page_get_generic
self.draft_page_set_func = self._draft_page_set_generic
评论区精华
Review 中主要讨论了以下设计权衡:
风险与影响
关联脉络
参与讨论