Prhub

#34080 config: retire the hidden global fallbacks and the mamba-extra-buffer instance reads

原始 PR 作者 ch-wan 合并时间 2026-08-10 05:43 文件变更 12 提交数 2 评论 17 代码增减 +112 / -83

执行摘要

移除隐藏全局配置回退,mamba 谓词改读配置袋

PR body 明确指出两类“看起来像别的、实际在读进程配置”的形态:optional-injection 惯用法 f(server_args=None) 后跟 server_args = get_server_args(),在“已注入实例”的外表下悄悄读进程配置,导致 read ratchet(按参数名判定调用者决策的机制)永远看不到这些读取;而 enable_mamba_extra_buffer / _lazy 本是两个已发布叶子(memory.disable_radix_cacheexec.mamba.mamba_radix_cache_strategy)的纯函数,十二个调用点没有理由绕道 startup record。作者自评中也强调这是“reader convergence”的一部分,最终让业务代码不再直读发布后的 ServerArgs。

值得精读,尤其是关注配置架构的工程师。本 PR 展示了三类可迁移的方法论:(1) 用 read ratchet 制度识别“可选注入”反模式,并让谓词归属到真正拥有数据的配置袋;(2) 通过 AST 扫描死绑定来做迁移清理的机械归因,避免靠肉眼遗留死代码;(3) 在 post-publish 与 mid-resolution 之间维持谓词唯一定义的双轨约定。对 mamba radix cache 或 DeepSeek MoE 双流图路径有维护需求的同学,建议顺带阅读 review 中关于测试 fixture 迁移的讨论。

讨论亮点

本 PR 的 17 条 review 评论主要来自 ch-wan 的 stack 自评(基于 review vehicle #34087)与 codex 机器审阅,核心交锋如下:

  • codex 连续三次指出“袋谓词绕过旧 mock”导致单测隔离运行时崩溃:prepare_for_decodemamba_extra_buffer_enabled() 会让 test_schedule_batch_prepare_for_decode.pyValueError: config namespace 'memory' not published。ch-wan 逐条核对后确认 MLX 与 boundary 两个用例真实失败并修复,而 schedule-batch 用例在 main 上已被 override_server_args 转换、不重现。
  • ch-wan 自评将 test_schedule_batch_prepare_for_decode 的失败标记为“单独合入的 blocker”,要求本 PR 折入修复或紧随合入 #34081;最终修复折入本 PR,独立 green。
  • 死绑定清理从“4 处”扩展为“AST 扫描 9 处”:ch-wan 用 AST 遍历包内 server_args = get_server_args() 且后续无 Load 的代码,准确定位本 PR 应删 4 处、留给 #34081 5 处,并顺带移除测试中冗余的 getter patch。
  • overrides.py 的注释确立了“谓词唯一定义”原则:mid-resolution 的 mamba_extra_buffer_of 与 post-publish 的 runtime_context.mamba_extra_buffer_enabled 是同一谓词在不同解析阶段的双轨实现,必须保持字段路径一致。

实现拆解

1. 移除 optional-injection 死参数

  • python/sglang/srt/speculative/spec_utils.pyspec_need_hidden_states(server_args=None) 的唯一定位调用者从不传参,去掉参数改为读 get_spec().speculative_algorithm / get_spec().enable_multi_layer_eagle
  • python/sglang/srt/models/deepseek_v2.py_can_dual_stream_graph(hidden_states, server_args=None)enable_eplb 改读 get_exec().moe.enable_eplb,谓词不再需要 config 参数,forward 中的调用同步去掉绑定。
  • python/sglang/srt/mem_cache/allocation_sizing.pyget_alloc_len_per_decode 的两个调用者均显式传 config,参数改为必填;模块唯一的“which config”决策点收敛到 get_alloc_reserve_per_decode

2. 新增 post-publish 配置袋谓词

  • python/sglang/srt/runtime_context.py:新增 mamba_extra_buffer_enabled()mamba_extra_buffer_lazy_enabled(),直接读 get_memory().disable_radix_cacheget_exec().mamba.mamba_radix_cache_strategy 两个已发布叶子。
  • python/sglang/srt/arg_groups/overrides.py:补全 mamba_extra_buffer_lazy_of 作为 lazy 变体,并明确 mamba_extra_buffer_of 是谓词的唯一定义:ServerArgs 成员委托它,runtime_context 访问器是其 post-publish 兄弟(因两个叶子落在不同 bag 无法直接复用)。

3. 十二处调用点迁移与死绑定清理

  • schedule_batch.pyprepare_for_extend / prepare_for_decodespec_utils.pyprepare_mamba_track_for_verify / spec_prepare_for_decodebatch_result_processor.py_handle_finish_state_updated_req / _mamba_prefix_cache_updateinkling.py__init__sconv.py_update_sconv_cache_for_draft_extend 全部改用袋谓词。
  • 顺带删除因迁移而失效的 server_args = get_server_args() 死绑定(AST 扫描确认本 PR 负责 4 处:prepare_for_extendprepare_for_decodeprepare_mamba_track_for_verifyInkling.__init__),其余 5 处留给 #34081。

4. 测试配套:从 fake getter 转为 publish 配置

  • test_batch_result_processor_mamba_boundary.py:改用 get_context().override_server_args(mamba_radix_cache_strategy="extra_buffer", mamba_track_interval=4) 发布配置,删除对 get_server_args / get_exec / get_observability / get_disagg 的冗余 patch。
  • test_schedule_batch_prepare_for_decode.py:改为发布 override_server_args,避免 config namespace 'memory' not published
  • test_attention_patching.py(MLX):改为 patch 新谓词 batch_result_processor.mamba_extra_buffer_lazy_enabled

5. 收敛度量

  • 全局配置读取计数从 45 降到 24;ratchet 与 runtime-context / server-args / models / spec 套件在 PR 边界全部通过(单独跑 225 passed / 38 skipped)。
文件 模块 状态 重要度
python/sglang/srt/runtime_context.py 配置层 modified 6.76
python/sglang/srt/speculative/spec_utils.py 投机解码 modified 6.89
python/sglang/srt/mem_cache/allocation_sizing.py 显存分配 modified 6.34
python/sglang/srt/managers/schedule_batch.py 调度器 modified 6.32
python/sglang/srt/models/deepseek_v2.py 模型层 modified 6.55
python/sglang/srt/arg_groups/overrides.py 覆盖配置 modified 5.68
python/sglang/srt/managers/scheduler_components/batch_result_processor.py 批处理 modified 5.51
python/sglang/srt/models/inkling_common/sconv.py 卷积层 modified 5.75
python/sglang/srt/models/inkling.py 模型层 modified 4.94
test/registered/unit/managers/test_batch_result_processor_mamba_boundary.py 测试 modified 5.26
test/registered/unit/managers/test_schedule_batch_prepare_for_decode.py 测试 modified 5.08
test/registered/unit/hardware_backend/mlx/test_attention_patching.py 测试 modified 4.5

关键符号

spec_need_hidden_states _can_dual_stream_graph mamba_extra_buffer_enabled mamba_extra_buffer_lazy_enabled get_alloc_len_per_decode get_alloc_reserve_per_decode mamba_extra_buffer_lazy_of prepare_mamba_track_for_verify _handle_finish_state_updated_req _mamba_prefix_cache_update _update_sconv_cache_for_draft_extend

关键源码片段

python/sglang/srt/speculative/spec_utils.py core-logic

代表性文件:`spec_need_hidden_states` 移除 optional-injection 死参数并改读 `get_spec()`;`prepare_mamba_track_for_verify` / `spec_prepare_for_decode` 迁移到袋谓词并删除死绑定。

def spec_need_hidden_states() -> bool:
    # STANDALONE 草稿不消费 `spec_info.hidden_states`(vanilla LLM)。
    # multi_layer_eagle、DFLASH、DSPARK 不通过 FutureMap 转发 hidden_states。
    # TODO(lsyin): 当 step == 1 时也可以跳过。
    spec = get_spec()
    if spec.speculative_algorithm in ("STANDALONE", "DFLASH", "DSPARK"):
        return False
    return not spec.enable_multi_layer_eagle
​
​
def prepare_mamba_track_for_verify(batch: ScheduleBatch) -> None:
    """在 TARGET_VERIFY forward 前从 reqs 重建 mamba track 索引。    投机批次会跳过 prepare_for_decode 中的刷新,且 filter/merge 会使这些
    字段置空,所以必须在 verify 前重建;同时清掉 mask,避免 extend 阶段的
    旧 mask 在 TARGET_VERIFY 中触发 in-forward 跟踪(跟踪改由
    commit_mamba_states_after_verify 完成)。    lazy 模式:收集 `mamba_lazy_spec_prepare` 规划的 track 位置;本函数运行
    在 forward 隔离区,因此不得修改 req/pool 状态。
    """
    if not mamba_extra_buffer_enabled():
        return
    track_positions = None
    if mamba_extra_buffer_lazy_enabled():
        track_positions = batch.mamba_lazy_spec_track_positions_cpu
        assert track_positions is not None and len(track_positions) == len(
            batch.reqs
        ), (
            "lazy spec verify without a track plan: mamba_lazy_spec_prepare "
            "must run in prepare_for_decode for every spec decode iteration"
        )
    set_mamba_track_indices_from_reqs(batch, track_positions)
    batch.mamba_track_mask = None
    batch.mamba_track_seqlens = None
python/sglang/srt/mem_cache/allocation_sizing.py data-contract

`get_alloc_len_per_decode` 参数从可选变为必填,模块级“哪个 config”决策点唯一收敛到 `get_alloc_reserve_per_decode`,是契约收紧的典型示范。

def get_alloc_len_per_decode(server_args: ServerArgs) -> int:
    # 参数现在是必填的:两个调用者本来就传入 config,模块内部的全局
    # 回退是死代码,移除后调用契约更清晰。
    if server_args.speculative_algorithm is None:
        return 1
​
    # 投机解码按 max(topk * num_steps, num_draft_tokens) 预留每步 decode
    # 的 KV 长度。
    spec_steps = server_args.speculative_num_steps or 1
    spec_topk = server_args.speculative_eagle_topk or 1
    spec_tokens = server_args.max_speculative_num_draft_tokens
    page_size = server_args.page_size
​
    from sglang.srt.speculative.spec_info import SpeculativeAlgorithm
​
    spec_algo = SpeculativeAlgorithm.from_string(server_args.speculative_algorithm)
    if page_size == 1 or spec_topk == 1 or not spec_algo.has_draft_kv():
        return max(spec_steps * spec_topk, spec_tokens)
    else:
        # spec v2 树形结构(page>1, topk>1):每个 topk 分支按页对齐的
        # 最坏占用为 ceil((page_size-1 + num_steps) / page) 页,且各分支
        # 相互复制,因此要为所有 topk 分支统一预留。
        num_new_pages_per_topk = (
            (page_size - 1) + spec_steps + page_size - 1
        ) // page_size
        return max(num_new_pages_per_topk * page_size * spec_topk, spec_tokens)
​
​
def get_alloc_reserve_per_decode(server_args: Optional[ServerArgs] = None) -> int:
    """每个请求在每个 decode 步预留的 KV 长度。    2x 是一个双缓冲,用于吸收 overlap 模式下 kv_committed_len 的滞后;
    请求路径上的调用者手中没有 config,因此本函数保留可选的全局读取,
    成为本模块唯一的“哪个 config”决策点,其下所有函数都显式传实例。
    """
    if server_args is None:
        server_args = get_server_args()
    return 2 * get_alloc_len_per_decode(server_args)

评论区精华

schedule_batch prepare_for_decode 测试隔离运行崩溃 测试

codex P2 指出 `mamba_extra_buffer_enabled()` 绕过测试对 `schedule_batch.get_server_args` 的 mock,单测单独运行会抛 `ValueError: config namespace 'memory' not published`。

结论:ch-wan 回复不重现:该测试在 main 上已改为 `override_server_args` 发布配置;同时确认 boundary 与 MLX 两个用例确实失败并修复。 · 已解决

MLX finished-request 测试依赖运行时 bag 测试

codex P2:`test_finished_request_snapshots_before_release` 仍 patch 旧 getter,单独跑 MLX 环境时新谓词读 bag 失败,只在同文件其他用例先发布配置时才碰巧通过。

结论:ch-wan 确认真实且将其归入本 commit 同步修复:MLX 测试改为 patch `batch_result_processor.mamba_extra_buffer_lazy_enabled`。 · 已解决

boundary 测试 fixture 仍用过期 mock 测试

codex P2:`test_batch_result_processor_mamba_boundary.py::test_overlap_scheduler_handles_zero_and_one_batch_lookahead` 单独运行时第一个 `prepare_for_decode()` 即触发 bag 未发布异常,fixture 仍 patch 旧 getter。

结论:ch-wan 将 fixture 改为 `override_server_args(mamba_radix_cache_strategy="extra_buffer", mamba_track_interval=4)` 并移除多个冗余 patch;最终本 PR 单独跑 225 passed / 38 skipped。 · 已解决

谓词迁移后遗留的 server_args 死绑定 设计

ch-wan self-review 指出谓词改读 bag 后,多处 `server_args = get_server_args()` 已无后续 Load,仍产生不必要的全局读取并误导测试 mock。

结论:用 AST 扫描确认 9 处死绑定,本 PR 删除 4 处(`prepare_for_extend`、`prepare_for_decode`、`prepare_mamba_track_for_verify`、`Inkling.__init__`),其余 5 处在 #34081 删除;同时清理测试中对应冗余 patch。 · 已解决

单独合入的 red-CI blocker 正确性

ch-wan 两次自评均标记:若本 PR 单独合入,`test_schedule_batch_prepare_for_decode` 会因 bag 未发布而失败,需折入 #34081 的 fixture 修复或紧随合入 #34081。

结论:修复已折入本 PR,独立合入即 green,不再依赖 #34081 先行。 · 已解决

谓词唯一定义与双轨职责划分 设计

`overrides.py` 的 docstring 强调 `mamba_extra_buffer_of` 是谓词唯一定义:ServerArgs 成员委托它,`runtime_context` 访问器是 post-publish 兄弟,因两个叶子落在不同 bag 无法直接复用。

结论:确立 mid-resolution(overrides)与 post-publish(runtime_context)双轨一致性约定,为后续 #34095 / #34096 的 bag 化迁移提供范式。 · 已解决

风险与影响

  • 配置发布时序风险:谓词从 ServerArgs 实例读取改为读配置袋后,若 memory / exec bag 尚未发布(单测隔离、启动早期、非标准初始化路径),会直接抛 ValueError: config namespace 'memory' not published。生产路径依赖 publish 先于首次调度完成,但单测必须同步改为 publish 或 patch 新谓词,三个测试文件的修改正说明该迁移面广。
  • DeepSeek MoE 双流图决策回归_can_dual_stream_graphenable_eplb 读取点从 server_args.enable_eplb 变为 get_exec().moe.enable_eplb,若 override 顺序或发布时机不同,可能导致双流图启用决策与旧行为不一致,影响 DeepSeek-V2 系列的性能路径。
  • 热路径开销prepare_for_decode / prepare_for_extend 在每步调度中调用袋谓词,本质是两次字典/属性查找 + 字符串比较,与原方法调用等价,性能风险低但属于核心调度路径变更。
  • 跨 PR 依赖:本 PR 是 stack 的第 1/6 环,部分死绑定与后续测试转换依赖 #34081;当前已自洽,但若单独回退或 cherry-pick 需连同测试修复一起。
  • 代码库结构:全局 ServerArgs 读取从 45 处降至 24 处,read ratchet 的制度盲区(optional-injection)被清除,后续新增代码更难“偷读”进程配置。
  • 开发者与测试者:凡是依赖 mamba-extra-buffer 开关的测试不能再 fake get_server_args(),必须 publish 配置袋或直接 patch 新谓词;mamba 相关单测的写法随之统一到 override_server_args
  • 功能与用户:行为等价,无 API 变化、无性能回退,用户无感知。
  • 团队演进:为同一系列的 #34081 / #34095 / #34096 铺路,确立了“解析管线用 ServerArgs 成员、业务代码用 runtime_context 袋谓词、overrides 提供 mid-resolution 等价物”的三层职责边界。
核心路径变更 跨 PR 依赖 测试夹具需同步迁移 行为等价重构

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论