执行摘要
- 一句话:修复 DeepSeek V4 HiCache 层计数逻辑并拆分测试 CI
- 推荐动作:此 PR 修复了关键的 PP + HiCache 兼容性问题,核心逻辑改动集中在层映射计算,值得精读以理解 PP 对缓存层的影响。同时应关注两个遗留风险:state pools 索引偏移和测试空覆盖,建议在后续 PR 中跟进修复。
功能与动机
PP 支持提交改变了 layer_mapping 结构,使其包含 None 条目,原有的 len(compression_ratios) 不再代表真实的活跃层数,导致 HiCache 在多层映射、sidecar pool 注册时访问到不存在的层,引发崩溃或数据错误。PR body 明确说明:“A previous PP support commit changed DeepSeekV4TokenToKVPool.layer_mapping to be stage-aware, leaving entries outside the active layer range as None.”
实现拆解
- 核心逻辑修复(hybrid_pool_assembler.py):将
build_deepseek_v4_hicache_stack 和 attach_hybrid_pool_to_unified_cache 中的 transfer_layer_num 从 len(kvcache.compression_ratios) 改为 kvcache.end_layer - kvcache.start_layer,并添加 TODO 备注后续支持 PP。
- 层映射切片修正:在枚举
kvcache.layer_mapping 构建 C4/C128 映射时,增加切片 [kvcache.start_layer : kvcache.end_layer],确保只处理当前 PP stage 拥有的层,避免访问 None 条目。
- 测试拆分与 CI 重排:
- 新增
test_unified_radix_cache_kl_hicache.py,将原本仅在 nightly 中运行的 Mamba 和 DeepSeek V4 HiCache 测试移至 base CI,注册为 base-c stage。
- 原
test_unified_radix_hicache_kl.py 重命名为 test_unified_radix_cache_kl_hicache_nightly.py,移除 Mamba 和 DeepSeek V4 测试,仅保留 GLM5 模型测试,并新增 GSM8KTwoPassMixin 用于验证精度稳定性。
关键文件:
python/sglang/srt/mem_cache/hybrid_cache/hybrid_pool_assembler.py(模块 缓存层;类别 source;类型 core-logic;符号 build_deepseek_v4_hicache_stack, attach_hybrid_pool_to_unified_cache): 核心修复文件:修改了 DeepSeek V4 HiCache 的 transfer_layer_num 计算和层映射枚举逻辑,确保 PP 场景下只处理当前 stage 的活跃层。
test/registered/radix_cache/test_unified_radix_cache_kl_hicache.py(模块 测试;类别 test;类型 test-coverage;符号 TestUnifiedMambaHiCache, setUpClass, tearDownClass, _assert_dsv4_decode_cached_tokens): 新增的 base CI 测试文件,将 Mamba 和 DeepSeek V4 HiCache 测试从 nightly 迁移过来,注册在 base-c stage,确保日常 CI 覆盖。
test/registered/radix_cache/test_unified_radix_cache_kl_hicache_nightly.py(模块 测试;类别 test;类型 rename-or-move;符号 TestUnifiedMambaHiCache, setUpClass, tearDownClass, _assert_dsv4_decode_cached_tokens): 重命名并精简后的 nightly 测试文件,仅保留 GLM5 模型测试,并添加 GSM8KTwoPassMixin 以验证精度稳定性。
关键符号:build_deepseek_v4_hicache_stack, attach_hybrid_pool_to_unified_cache
关键源码片段
python/sglang/srt/mem_cache/hybrid_cache/hybrid_pool_assembler.py
核心修复文件:修改了 DeepSeek V4 HiCache 的 transfer_layer_num 计算和层映射枚举逻辑,确保 PP 场景下只处理当前 stage 的活跃层。
# python/sglang/srt/mem_cache/hybrid_cache/hybrid_pool_assembler.py
def build_deepseek_v4_hicache_stack(
*,
params: CacheInitParams,
server_args: ServerArgs,
kvcache: Any,
page_size: int,
tp_group,
load_cache_event,
attn_cp_group=None,
attn_tp_group=None,
storage_backend: Optional[str],
host_swa_evict_fn=None,
device_swa_evict_fn=None,
prefetch_threshold: int = 256,
model_name: Optional[str] = None,
storage_backend_extra_config: Optional[dict] = None,
pp_rank: int = 0,
pp_size: int = 1,
enable_storage_metrics: bool = False,
) -> tuple[HostPoolGroup, HybridCacheController]:
# 使用 end_layer - start_layer 替代 len(compression_ratios)
# 确保在多 PP stage 下只计算当前 stage 拥有的层数
transfer_layer_num = kvcache.end_layer - kvcache.start_layer
full_layer_mapping = {layer_id: layer_id for layer_id in range(transfer_layer_num)}
swa_layer_mapping = {
layer_id: layer_id for layer_id in range(len(kvcache.swa_kv_pool.kv_buffer))
}
c4_layer_mapping = {}
c128_layer_mapping = {}
c4_state_global_layers = []
c128_state_global_layers = []
# 对 layer_mapping 切片,只处理当前 stage 的层(避免访问 None 条目)
for layer_id, layer_item in enumerate(
kvcache.layer_mapping[kvcache.start_layer : kvcache.end_layer]
):
if layer_item.compress_ratio == 4:
c4_layer_mapping[layer_id] = layer_item.compress_layer_id
c4_state_global_layers.append(layer_id)
elif layer_item.compress_ratio == 128:
c128_layer_mapping[layer_id] = layer_item.compress_layer_id
c128_state_global_layers.append(layer_id)
# ... 后续使用 c4_state_global_layers 索引 kvcache.compress_state_pools
# 注意:review 指出此处 layer_id 为局部索引,可能引起 PP 下 state pools 访问错误
评论区精华
gemini-code-assist[bot] 在 review 中指出了两个高优问题:
- state pools 索引风险:在
hybrid_pool_assembler.py 中,切片后 layer_id 从 0 开始计数,但 c4_state_global_layers 仍用此局部索引访问全局 compress_state_pools,在 PP 模式下会访问错误的 state pools。该评论标记为高优先级,但未在后续提交中修复。
- 测试空覆盖:新增的测试类中存在重复的
test_gsm8k 定义,且都使用 pass 实现,导致继承自 UnifiedRadixTreeTestMixin 的实际测试被静默禁用,测试套件会误报成功。建议改用 @unittest.skipIf 装饰器调用 super()。
ShangmingCai 最后批准了 PR,评论“LGTM, let's wait for the CI.”
- State pools 索引风险 (correctness): 未在代码中修复,PR 仍被批准合并。
- 测试类中重复定义 test_gsm8k 并使用 pass (testing): 未修复,PR 被批准合并。
- TestUnifiedDeepSeekV4FlashHiCache 同样问题 (testing): 未修复,PR 被批准合并。
风险与影响
关联脉络
- PR #24704 feat: add Pipeline Parallelism (PP) and PD support for DeepSeek-V4: PP 支持改变了 DeepSeekV4TokenToKVPool.layer_mapping 结构,导致 HiCache 层计数失效,是此 bug 的直接诱因。
参与讨论