Prhub

#29191 [HiCache & HybridModel] nixl hicache backend support hybrid models

原始 PR 作者 ziruiliu 合并时间 2026-07-14 00:09 文件变更 4 提交数 5 评论 16 代码增减 +702 / -17

执行摘要

HiCache NIXL 后端支持混合模型

此前 PR #25538 为 NIXL 后端添加了混合模型存储支持,但 NIXL 后端经历了大规模重构,迁移了注册、host memory 路径、O_DIRECT 对齐处理和 bounce buffer 逻辑。本 PR 在重构后的后端结构上重新实现混合存储,使混合模型(如 Qwen3.5/Mamba 和 DSA 风格侧池)能够使用 NIXL 作为 HiCacheHybrid 控制器的存储后端。

此 PR 实现了 NIXL 后端对混合模型存储的关键支持,设计上考虑了零拷贝安全性、per-pool 上下文独立性和 sidecar 键命名规范。值得关注的设计决策包括对齐检测条件、持久 bounce buffer 方向分离和 v2 接口的池感知逻辑。建议相关开发者仔细阅读 hicache_nixl.py 中的 register_mem_host_pool_v2batch_get_v2 实现,以及 memory_pool_host.py 中的 is_stride_page_aligned 模式。对于生产部署,注意 dtype 风险尚未在 PR 中明确修复,需要后续跟进。

讨论亮点
  • lluki 关于 registered_pools 初始化:建议在基类 __init__ 中初始化,避免父类未初始化。作者采纳并改为在 HiCacheNixl.__init__ 中初始化。(已解决)
  • chatgpt-codex-connector 关于 dtype 一致性问题:指出 bounce buffer 使用 host_pool.dtype 可能导致 byte 到 fp 转换,存储脏数据(P1 严重性)。(未解决)
  • lluki 关于 batch_v2 代码复用:建议 batch_get_v2 调用 batch_get_v1 减少 Python 循环,作者拒绝以保持范围,同意作为未来优化。(已解决)
  • lluki 要求测试验证:要求在 v2 API 上增加 set-then-get 测试用例验证 PoolName 隔离。作者承诺添加,后续 commit 已补充。(已解决)

实现拆解

  1. 新增 _HybridPoolContext 数据类:在 hicache_nixl.py 中定义,为每个 side-pool 维护零拷贝状态、bounce buffer 和分页大小,统一管理传输上下文。
  2. 实现 register_mem_host_pool_v2 注册流程:跳过 KV 池(由 v1 处理),对 side-pool 调用 _hybrid_pool_supports_zero_copy 检查零拷贝可行性;若支持则预注册 host 缓冲区到 NIXL agent,否则分配持久 bounce buffer(区分方向)。
  3. 添加 sidecar 键命名函数_get_component_key_get_hybrid_component_keys 根据池类型(Mamba: _mamba_temporal/_mamba_conv_N;DSA: _k/_v)生成多文件后缀,确保对象名唯一。
  4. 重写 v2 传输接口batch_get_v2/batch_set_v2/batch_exists_v2 使用 per-pool 上下文执行零拷贝或 bounce buffer 方式传输,并输出调试统计(如 batch_set_v2[mamba] transferred:...)。
  5. 为侧池类添加对齐检测:在 memory_pool_host.py 中为 MambaPoolHostHostKVCacheDeepSeekV4StateHostPoolDSAIndexerPoolHostHostPoolGroup 实现 is_stride_page_aligned 方法,零拷贝决策依赖它。
  6. 配套更新:新增单元测试(MockHybridPool,覆盖注册、set/get、exists),更新 NIXL README 提供验证命令和期望输出。
文件 模块 状态 重要度
python/sglang/srt/mem_cache/storage/nixl/hicache_nixl.py 存储后端 modified 8.84
python/sglang/srt/mem_cache/memory_pool_host.py 内存池 modified 7.14
test/registered/unit/mem_cache/test_hicache_nixl_storage.py 测试 modified 7.12
python/sglang/srt/mem_cache/storage/nixl/README.md 文档 modified 3.37

关键符号

register_mem_host_pool_v2 _hybrid_pool_supports_zero_copy _get_bounce_slot_buffers batch_get_v2 batch_set_v2 batch_exists_v2 _get_component_key _get_hybrid_component_keys is_stride_page_aligned

关键源码片段

python/sglang/srt/mem_cache/storage/nixl/hicache_nixl.py core-logic

核心实现文件,新增混合模型支持的所有逻辑。

@dataclass
class _HybridPoolContext:
    '''每个注册的 side-pool 拥有自己的上下文,记录 host pool 引用、
    是否启用零拷贝,以及非零拷贝时使用的持久 bounce buffer。'''
    host_pool: HostKVCache
    is_zero_copy: bool
    bounce_set: Optional[torch.Tensor] = None
    bounce_get: Optional[torch.Tensor] = None
    bounce_page_bytes: int = 0
​
​
def register_mem_host_pool_v2(self, host_pool: HostKVCache, host_pool_name: PoolName) -> None:
    '''注册 side-pool(如 Mamba、DSA)到 NIXL 存储。    跳过 KV 池(由 v1 处理)。对 side-pool 检查零拷贝是否安全:
    调用 _hybrid_pool_supports_zero_copy。如果安全,预注册 host buffer
    到 NIXL agent,设置 is_zero_copy=True;否则分配持久 bounce buffer
    (每个方向独立),避免跨池共享。
    '''
    if host_pool_name == PoolName.KV:
        return
    super().register_mem_host_pool_v2(host_pool, host_pool_name)
​
    is_zero_copy = self._hybrid_pool_supports_zero_copy(host_pool)
    ctx = _HybridPoolContext(host_pool=host_pool, is_zero_copy=is_zero_copy)
​
    if is_zero_copy:
        # 预注册池的 host buffer 到 NIXL agent,用于零拷贝传输
        regs = self.registry.register_hybrid_host_region(host_pool)
        self._host_regs.extend(regs)
    else:
        # 分配持久 bounce buffer,大小等于 host_pool 页面
        sample = host_pool.get_dummy_flat_data_page()
        page_bytes = sample.numel() * sample.element_size()
        ctx.bounce_set, ctx.bounce_get, ctx.bounce_page_bytes = (
            self._get_bounce_slot_buffers(page_bytes, needs_page_alignment=self.needs_page_alignment)
        )
​
    self._hybrid_pool_ctx[host_pool_name] = ctx
    logger.info(
        'HiCacheNixl: registered hybrid host pool %s zero_copy=%s',
        host_pool_name, is_zero_copy,
    )

评论区精华

registered_pools 初始化位置 设计

lluki 建议在基类 __init__ 中初始化 registered_pools 字典,避免子类初始化延迟。

结论:作者在 HiCacheNixl.__init__ 中初始化了 self.registered_pools。 · 已解决

dtype 不一致导致 side-pool 数据损坏 正确性

chatgpt-codex-connector 指出 bounce buffer 使用 host_pool.dtype 而非 torch.uint8,可能导致 byte 到 float 转换,NIXL 存储脏数据(P1 严重性)。

结论:作者未直接回复该评论,风险未在 PR 中明确修复。 · unresolved

batch_get_v2 与 batch_set_v2 代码复用 性能

lluki 建议 batch_get_v2 调用 batch_get_v1 以减少 Python 循环,并指出可以通过批量 NIXL xfer 提升性能。作者认为风险较高,保持当前范围作为未来优化。

结论:作者拒绝,保持现状,lluki 同意。 · 已解决

v2 set-then-get 测试覆盖 测试

lluki 要求增加一个 set-then-get 测试用例,验证不同 PoolName 隔离正确性(set Pool1 key data1; set Pool2 key data2; assert get Pool1 equals data1)。

结论:作者承诺添加,已在后续 commit 中补充。 · 已解决

风险与影响

  • 数据类型转换风险(P1)chatgpt-codex-connector 指出 bounce buffer 分配时使用 host_pool.dtype 而非 torch.uint8,可能导致 float 转换存储脏数据。若未修复,混合模型恢复的缓存数据可能无效。
  • Python 循环性能开销batch_get_v2/batch_set_v2 按池逐个循环提交 NIXL 传输,未批量合并,大规模请求时可能成为瓶颈。
  • 对齐检查依赖面广:零拷贝决策依赖于 is_stride_page_aligned 在各池类中的正确实现,遗漏或错误将影响性能或正确性。
  • DSV4 逻辑锚点兼容性:DeepSeek V4 使用 LogicalHostPool 作为 KV 锚点(kv_buffer=None),需特殊处理以避免 NIXL 注册失败(已在第4个 commit 修复)。
  • 用户:使用 NIXL 后端的混合模型(如 Qwen3.5/Mamba、DSA 模型)现可启用 HiCache 缓存,提升推理响应速度。零拷贝路径降低内存拷贝开销。
  • 系统:新增 register_mem_host_pool_v2 协议和 v2 GET/SET/EXISTS 接口,不影响现有 v1 路径;对 NIXL 后端增加配置复杂度(对齐要求、sidecar 命名)。
  • 团队:文档增加了验证步骤,测试覆盖了 hybrid 场景,降低了未来维护难度。
数据类型转换风险 Python 循环性能开销 对齐检查依赖广 DSV4 逻辑锚点特殊处理

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论