# PR #34266 完整报告

- 仓库：`sgl-project/sglang`
- 标题：config: the alias form of the runner-side instance read
- 合并时间：2026-08-15 15:39
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/34266

---

# 执行摘要

- 一句话：别名形式 server_args 读取全部迁移到配置 bag，新增派生访问器
- 推荐动作：值得精读。这是理解 SGLang runtime context 配置迁移（step-12 系列）如何系统性收口 runner 侧读取面的关键 PR，尤其推荐关注三处：bag 访问器与 ServerArgs 成员的等价性如何由测试盯测；GDN 发布后读回导致幂等性破坏的根因与修复方式；以及 configured_pp_size() 这类“配置值 vs 活拓扑”的取舍与 ratchet 登记。

# 功能与动机

PR body 明确指出：上一个批次只统计了 self.server_args.X，便声称 runner 表面已迁移完毕，但同样的读取通过局部别名拼写（server_args = model_runner.server_args 后接 server_args.leaf）时，grep 看不到；AST 普查在 11 个文件中共找出 57 处此形式读取。目标是让所有进程级配置读取统一走已发布的 bag，使 post-publish override 语义一致，并为后续 step-12 的迁移与盯测（ratchet）提供准确基线。

# 实现拆解

1. **AST 普查与分类**：对 11 个文件中的别名读取逐函数计数，52 处为叶子读取（spec 11、schedule 9、memory 7、exec.graph 5、exec.moe 5、parallel 4、disagg 4、model 3、exec.mamba 2、exec.overlap 2），5 处为非叶子（3 个派生成员、get_attention_backends()、以及一个只重名的 server_args_dict）。
2. **叶子读取迁移**：将别名转换为对应 bag 访问器。典型如 pool_configurator.py 的 SWAChunkCapPoolConfigurator 构造函数，sa.speculative_algorithm 等改为 get_spec() / get_schedule() / get_disagg() / get_memory()；flashinfer_autotune.py 的开关与 MoE backend 字符串改为 get_exec().kernel / get_exec().deterministic / get_exec().moe / get_spec()；flashattention_backend.py 的 seed 读取与 fa_skip_kv_cache 判断改为 get_spec() / get_exec() / get_model() / get_schedule() / get_memory()。
3. **派生成员与访问器收口**：runtime_context.py 新增 max_prefill_buffer_tokens() 访问器，由 schedule 叶子与 configured PP 大小推导；eager_runner.py 改用 max_speculative_num_draft_tokens()、mamba_extra_buffer_enabled()、max_prefill_buffer_tokens()、get_parallel().dp_size；attention_backend_setup.py 的 build_attention_backends 与 resolve_attention_backend_strs 改为读 attention_backends() 与 get_disagg() / get_exec()。
4. **两项语义修正**：GDN 自动默认值改为让 initialize_linear_attn_config 也读 bags，使默认值经已发布 override 传播并在 TBO 三次副本初始化中幂等；dispatch_event_loop 三个 PP 分支改用 configured_pp_size()，避免 MLX stub 未初始化 torch.distributed 时触发实际拓扑属性断言。
5. **测试与盯测配套**：test_pool_configurator.py、test_registry.py、test_gdn_prefill_backend_policy.py 由灌注 SimpleNamespace / MagicMock record 改为 per-case override_server_args + addCleanup 恢复；test_runtime_context.py 新增对 max_prefill_buffer_tokens() 的 48 case 派生断言；test_global_config_read_ratchet.py 登记 configured_pp_size() 等新调用点，exposure ratchet 同步移除已消失的读取对。

关键文件：
- `python/sglang/srt/runtime_context.py`（模块 配置访问；类别 source；类型 dependency-wiring；符号 max_prefill_buffer_tokens）: 配置 bag 访问器的核心载体，新增 max_prefill_buffer_tokens() 派生访问器，把 schedule 叶子与 configured PP 大小统一为可被 override 的单一数据源。
- `python/sglang/srt/model_executor/pool_configurator.py`（模块 池配置器；类别 source；类型 data-contract；符号 SWAChunkCapPoolConfigurator.__init__, SWAChunkCapPoolConfigurator.is_applicable）: SWAChunkCap 配置器从 kvc.server_args 别名读取全部改为 bag 读取，spec decode 分配改走纯关键字函数，是别名迁移的代表性样本。
- `python/sglang/srt/layers/attention/flashattention_backend.py`（模块 注意力后端；类别 source；类型 core-logic；符号 _should_disable_scheduler_metadata_precompute, FlashAttentionBackend.__init__）: FlashAttention 构造函数中的 seed 读取与 fa_skip_kv_cache 判断全部迁移到 bags，_should_disable_scheduler_metadata_precompute 移除 record 参数并自行读取 parallel 叶子。
- `python/sglang/srt/model_executor/runner/flashinfer_autotune.py`（模块 自动调优；类别 source；类型 data-contract；符号 should_run_flashinfer_autotune, get_flashinfer_autotune_skip_ops, flashinfer_autotune_cache_path）: flashinfer 自动调优的开关、确定性门控与 MoE backend 字符串从 server_args 改为 exec / spec bag，注释同步更新以标明数据来源。
- `python/sglang/srt/managers/scheduler.py`（模块 调度器；类别 source；类型 core-logic；符号 dispatch_event_loop）: dispatch_event_loop 的三个 PP 分支改用 configured_pp_size()，避免 MLX 事件循环在 torch.distributed 未初始化时触发实时拓扑断言。
- `python/sglang/srt/utils/common.py`（模块 公共工具；类别 source；类型 core-logic；符号 spec_decode_alloc_len_per_request）: spec_decode_alloc_len_per_request 从接收 server_args 对象改为纯关键字叶子，成为无副作用纯函数，便于从 bag 取值调用。
- `python/sglang/srt/model_executor/runner/eager_runner.py`（模块 执行器；类别 source；类型 data-contract；符号 EagerRunner.__init__）: EagerRunner 初始化使用派生访问器与 bag 叶子，是 CUDA graph 缓冲注册与 token 上限尺寸的前置路径。
- `test/registered/unit/model_executor/test_pool_configurator.py`（模块 池配置测试；类别 test；类型 test-coverage；符号 _publish_config, _make_model_runner）: 测试夹具从注入 SimpleNamespace record 改为发布配置并 per-case 恢复，是本次三套测试迁移的代表，也覆盖了 SWAChunkCap 各类分支。
- `test/registered/unit/test_runtime_context.py`（模块 配置访问测试；类别 test；类型 test-coverage；符号 test_prefill_buffer_ceiling_matches_the_member）: 新增 test_prefill_buffer_ceiling_matches_the_member，以 48-case 矩阵把 max_prefill_buffer_tokens() 访问器与 ServerArgs 成员钉死等价。

关键符号：max_prefill_buffer_tokens, spec_decode_alloc_len_per_request, _should_disable_scheduler_metadata_precompute, should_run_flashinfer_autotune, build_attention_backends, dispatch_event_loop

## 关键源码片段

### `python/sglang/srt/runtime_context.py`

配置 bag 访问器的核心载体，新增 max_prefill_buffer_tokens() 派生访问器，把 schedule 叶子与 configured PP 大小统一为可被 override 的单一数据源。

```python
def max_prefill_buffer_tokens() -> int:
    """The prefill-buffer ceiling: chunked_prefill_size, except PP dynamic
    chunking can grow chunks toward max_prefill_tokens and probe at 1.25x.
    该访问器完全从已发布的 bag 推导，因此会跟随 post-publish override。
    """
    import math

    schedule = get_schedule()
    chunked = (
        schedule.chunked_prefill_size
        if schedule.chunked_prefill_size and schedule.chunked_prefill_size > 0
        else 0
    )
    tokens = chunked
    if (
        schedule.enable_dynamic_chunking
        and _configured_parallel("pp_size") > 1  # 配置值而非活拓扑，MLX 场景安全
        and chunked
    ):
        # PP 动态分块时，chunk 可向 max_prefill_tokens 增长并上探 1.25x
        tokens = max(
            tokens, schedule.max_prefill_tokens or 0, math.ceil(chunked * 1.25)
        )
    return tokens

```

### `python/sglang/srt/model_executor/runner/flashinfer_autotune.py`

flashinfer 自动调优的开关、确定性门控与 MoE backend 字符串从 server_args 改为 exec / spec bag，注释同步更新以标明数据来源。

```python
def should_run_flashinfer_autotune(
    model_runner: ModelRunner, *, for_speculative_draft: bool = False
) -> bool:
    """Check if flashinfer autotune should be run."""
    mr = model_runner
    if mr.device != "cuda":
        return False
    # 开关型叶子改从 exec bag 读取，行为与 server_args 等价
    if get_exec().kernel.disable_flashinfer_autotune:
        return False
    if get_exec().deterministic.enable_deterministic_inference:
        # 确定性模式下不调优：tuned configs 按问题 shape 变化，会破坏归约顺序
        return False

    if for_speculative_draft:
        # draft 侧叶子走 get_spec()，target 侧叶子走 get_exec().moe
        backend_str = (
            get_spec().speculative_moe_runner_backend
            or get_exec().moe.moe_runner_backend
        )
        a2a_backend_str = (
            get_spec().speculative_moe_a2a_backend
            or get_exec().moe.moe_a2a_backend
        )
    else:
        backend_str = get_exec().moe.moe_runner_backend
        a2a_backend_str = get_exec().moe.moe_a2a_backend

    # CuteDSL v1 绕过 MoeRunner，其 dummy dispatch 可能超过 DeepEP 低延迟上限
    if backend_str == "flashinfer_cutedsl" and a2a_backend_str == "deepep":
        return False

    moe_needs_autotune = backend_str in [
        "flashinfer_trtllm",
        "flashinfer_trtllm_routed",
        "flashinfer_mxfp4",
        "flashinfer_cutedsl",
        "flashinfer_cutlass",
    ]
    ...

```

# 评论区精华

Codex 评审捕获了本 PR 中两个最关键的语义回归：一是 GDN 自动默认值在 TBO 多副本下失去幂等性——将 guard 改成读取 bag 叶子后，函数自身发布的自动默认值被读回为“已配置”，导致后续副本跳过默认值并回退 Triton；二是 dispatch_event_loop 改用 get_parallel().pp_size 后，MLX stub 因从不初始化 torch.distributed 而直接触发断言。两个问题均在合入前修复并补充测试。作者自审还发现 SWAChunkCap 的 spec decode 分配尺寸仍从 handed record 读取，与 bag 守卫存在 override 可见性不一致，于是将 spec_decode_alloc_len_per_request 改为纯关键字入参。测试侧则统一了 per-case publish + addCleanup 的恢复模式，避免 last-publish-wins 污染同进程后续用例。

- GDN 自动默认值在 TBO 多副本下失去幂等性 (correctness): 将 initialize_linear_attn_config 的投影也改为读 bags 并移除参数，默认值经已发布 override 传播，所有副本落到同一值；test_linear_attn_config.py 改为 publish 并固定三次连续初始化仍保持 flashinfer。
- MLX 事件循环调度读取实际 PP 大小导致断言崩溃 (correctness): dispatch_event_loop 三个 PP 分支全部改用 configured_pp_size() 读取配置叶子，并在 _CONFIGURED_SIZE_CALL_SITES 登记理由；ratchet 机制捕获了未登记的首版实现。
- SWAChunkCap 的 spec decode 分配尺寸仍来自 handed record (design): spec_decode_alloc_len_per_request 改为纯关键字入参并全部取 get_spec() 叶子；spec-v2 路径读取 max_speculative_num_draft_tokens()；sa = kvc.server_args 绑定从文件移除。
- 测试夹具发布后的恢复模式 (testing): 三个套件（GDN prefill policy、cache registry、pool configurator）统一改为 per-case _publish(testcase, **fields) + addCleanup(override.restore)，sps-table 套件同步采用相同形态。
- 过时注释清理 (documentation): 两处注释均已更新，分别标明从 spec bag / exec.moe 叶子读取与回退到已发布 spec bag。

# 风险与影响

- 风险：风险集中在配置语义与启动路径：
 1) dispatch_event_loop 改用 configured_pp_size() 后，若实际拓扑与配置值在动态场景下不一致，事件循环选择可能与真实并行组不符（当前预期二者一致，但需关注未来动态并行）；
 2) GDN 自动默认值的正确性依赖发布 override 的传播时序，initialize_linear_attn_config 与 backend 副本之间的读取顺序若被后续改动打破，可能再次出现 Triton/FlashInfer 回退不一致；
 3) 测试夹具改为全局限定 override 后，同进程内其他测试在窗口期会观察到被覆盖的配置值，虽有 addCleanup 兜底，但仍属共享可变状态；
 4) flashattention_backend.py 与 eager_runner.py 位于模型初始化与 CUDA graph 捕获核心路径，任何 bag 叶子与 ServerArgs 成员的不等价都会直接改变缓冲尺寸或确定性开关。
 - 影响：影响面覆盖模型启动、内存池 /SWA 池尺寸、flashinfer 自动调优、CUDA graph 捕获批次选择、事件循环调度与 GDN 后端选择，属于核心运行路径的配置读取面重构。对用户默认行为保持等价（作者验证 Qwen3-Next GDN 启动与 base 字节一致），但为后续 post-publish override 与配置迁移提供了统一基线。对团队而言，该 PR 确立了几项可复用模式：别名读取普查方法、派生访问器 + TestDerivedPredicatesAgreeAcrossTiers 盯测、configured_pp_size() 调用点 ratchet 登记，以及测试夹具 per-case publish 恢复范式。
 - 风险标记：核心路径变更 , 配置语义迁移 , 测试全局状态隔离 , 启动路径依赖

# 关联脉络

- PR #34267 config: pin the supplied-instance surface that a raw record would change: 同一步骤 12 配置迁移系列，定义暴露面盯测与 EPD 扳机；本 PR 中 _should_disable_scheduler_metadata_precompute 参数移除后 exposure ratchet 报告 pin pair 为 gone，正是双向 pin 在起作用。
- PR #34269 config: state the bag contract as what resolution produced, and the skill rule that goes with it: 同系列，重写 bag 契约测试与 skill 规则，为本 PR 的别名读取迁移提供契约依据与测试基础设施。
- PR #34819 config: the post-publish consumers of the supplied-instance surface read the bags: 本 PR 的后续部分：把 publish 后消费者从 server_args 读取迁移到 bags，补完本 PR 未覆盖的输入面，两 PR 共享 model_runner / speculative worker 等文件的迁移脉络。
- PR #34913 [CI] Move the static ratchets back to CPU unit tests: 与本 PR 共享 test_global_config_read_ratchet.py 与静态 ratchet 机制；本 PR 在其中登记 configured_pp_size() 等新调用点。