Prhub

#36972 config: the resolution callbacks into the record go to zero

原始 PR 作者 ch-wan 合并时间 2026-08-29 19:18 文件变更 50 提交数 5 评论 2 代码增减 +1087 / -979

执行摘要

清零 arg_groups 对 ServerArgs 的 96 处回调用例

PR body 明确说明:在 #36792 之后解析管线已迁入 arg_groups/,但仍大量回读 record——普查发现 24 个 ServerArgs 成员上有 96 个调用点、503 行代码,其中 14 个成员除了解析期助手外没有任何其他调用者,纯属历史遗留。作者目标是让 arg_groups/ 对 record "no method call and no property read",使依赖方向单一化。

值得精读。本 PR 是配置系统架构收敛的关键一环,展示了如何系统性消除跨层反向依赖。重点关注:(1) test_no_hook_reads_a_field_off_the_record 包级守卫的设计思路;(2) from ... import 拷贝语义对测试接缝的影响分析;(3) memo 放在 record 上随 pickle 传递的取舍。同时建议跟踪 Codex 指出的 cutedsl_moe_max_num_tokens 遗漏调用是否已在后续修复中解决。

讨论亮点

唯一实质 review 来自 Codex bot 的 P1 建议:

Update the remaining CuTeDSL budget caller — When Qwen3.5 FlashInfer MNNVL CuTeDSL fusion prepares its workspace, resolve_max_m() still calls server_args.cutedsl_moe_max_num_tokens() in qwen35_flashinfer_fusion.py:40. This commit removes that ServerArgs method and only introduces this free function, so a real ServerArgs instance raises AttributeError during prepare_qwen35_flashinfer_fusion() before CUDA-graph capture.

即 commit 5 删除方法后遗漏了一处运行时调用方,该处会在 CUDA graph 捕获前抛 AttributeError。PR 已合并,该问题需要在后续修复中跟进。

实现拆解

本 PR 按 5 个 commit 分五步完成迁移:

  1. Resolution-only 助手移入所属 hook 家族(commit 1)
    - generate_{decode,prefill,cpu}_*_batch_sizesapply_cuda_graph_disaggregation_roles 移入 cuda_graph_hook.py
    - reserve_for_graph_mbreserve_for_deepep_a2a_mbadjust_mem_fraction_for_vlm 移入 memory_hook.py
    - 两个 dispatch-token 预算移入 moe_hook.pyis_mistral_native_format 移入 model_path_hook.pyis_attention_backend_not_setget_default_attn_backend 移入 overrides.py
    - 关键的 get_default_attn_backend 原先是读 record 上的 tp_size(原始输入),而非解析决定的结果,迁移后改为通过 view 读取,顺带修复了该缺陷。

  2. 声明接缝脱离 record(commit 2)
    - _resolved_declare_late_resolution 三个仅做转发的成员方法删除,61 个调用点改为直接调用 resolved_viewdeclare_resolutiondeclare_late_resolution

  3. Attention backend 对合并为一个函数(commit 3)
    - _resolved_attention_backendsget_attention_backends 合并为 attention_backends_of(resolved_view(record))
    - use_mla_backendshould_report_expert_balancedness 变为自由函数。

  4. 模型配置构建改为函数(commit 4)
    - get_model_config(56 个调用点)变为 overrides.model_config_of
    - memo(_model_config 等)留在 record 上作为 resolution 中间产物,因为需要随 pickle 传到子进程,但回调本身离开。

  5. 最后三个计算读加最后两个属性(commit 5)
    - post_capture_kv_sizing_plannedcutedsl_moe_max_num_tokensmax_prefill_buffer_tokens 改为纯函数,发布后的读取方改走 runtime_context 中的既有兄弟函数;
    - mamba_cache_chunk_sizemax_speculative_num_draft_tokens 两个 property 同样迁移,因为属性读取不计入函数调用普查,这两个是补漏。

测试与接缝配套:新增 test_no_hook_reads_a_field_off_the_record 包级守卫测试,遍历 arg_groups/ 防止未来回读;use_mla_backend 等改为调用点导入,使 patch("...overrides.use_mla_backend") 成为单一测试接缝;hisparse_hook._is_hip 改为向 utils.common 询问平台探针。

未覆盖的一处:Codex review 指出 qwen35_flashinfer_fusion.py:40 仍调用 server_args.cutedsl_moe_max_num_tokens(),迁移后该实例会抛 AttributeError,需要在后续 commit 中把该调用方改为新自由函数。

文件 模块 状态 重要度
python/sglang/srt/server_args.py 配置解析 modified 8.93
python/sglang/srt/arg_groups/overrides.py 配置解析 modified 8.93
python/sglang/srt/arg_groups/cuda_graph_hook.py CUDA 图 modified 8.7

关键符号

model_config_of record_of attention_backends_of use_mla_backend post_capture_kv_sizing_planned cutedsl_moe_max_num_tokens generate_decode_cuda_graph_batch_sizes generate_prefill_cuda_graph_batch_sizes generate_cpu_graph_batch_sizes apply_cuda_graph_disaggregation_roles is_mistral_native_format required_mori_dispatch_tokens_per_rank required_pplx_dispatch_tokens_per_rank

关键源码片段

python/sglang/srt/server_args.py dependency-wiring

核心减重对象:删除 624 行、29 个成员方法,`_declare`、`_apply_cuda_graph_disaggregation_roles`、`post_capture_kv_sizing_planned` 等全部迁出,是本次重构的 " 减法 " 主战场。

# python/sglang/srt/server_args.py —— 迁移后的 record 边界# 本 PR 的核心减法:以下成员方法全部删除,调用方改走 arg_groups 自由函数。
# 删除后 record 只保留字段、原始输入快照、resolution stash 与 memo。# 删除前(base 版本)的形态示例:
# def _declare(self, source: str, **fields: Any) -> None:
# """This record's handlers declaring their resolution writes."""
# from sglang.srt.arg_groups.overrides import declare_resolution
# declare_resolution(self, source, **fields)
#
# 删除后:61 个调用点直接调用 arg_groups.overrides.declare_resolution(record, ...)# 删除前:
# def post_capture_kv_sizing_planned(self) -> bool:
# """Whether KV sizing was decided post-capture."""
# ...
# 删除后:同逻辑移入 overrides.py 的自由函数 post_capture_kv_sizing_planned(record),
# 且发布后读取方一律走 runtime_context 中的既有兄弟函数。# 关键保留决策:model_config_of 的 memo(_model_config / _model_config_built_from)
# 仍留在 record 上 —— 它是 resolution 中间产物,必须随 pickle 传到子进程,
# 且解析期没有其他 per-record 存储位置。离开的是回调,不是缓存。def replace_resolved(self, source: str, **changes: Any) -> ServerArgs:
    """A copy of this record that stays resolved, and says what it changed.    `dataclasses.replace` 构建的新实例不带 raw snapshot、stash 与 finished 标志,
    下一次 publish 会重新解析并丢掉所有决议;Ray 路径替换 dist_init_addr 时走到这里。
    变更追加进 stash 而非直接写字段:投影读 raw snapshot + declarations,
    直接写字段会让 copy 自己的决议发布成 parent 的 raw 值。
    """
    # 显式枚举实例上除字段外的一切:raw snapshot、stash、以及解析期 memo ——
    # 包括 model-configuration memo,copy 直接继承而非重建。
    field_names = {field.name for field in dataclasses.fields(self)}
    for name, value in vars(self).items():
        ...
    object.__setattr__(replacement, "_resolution_finished", True)
    return replacement
python/sglang/srt/arg_groups/cuda_graph_hook.py core-logic

CUDA graph 相关助手集中迁入:`generate_prefill/decode/cpu_cuda_graph_batch_sizes` 与 `apply_cuda_graph_disaggregation_roles` 落位,并把 `get_model_config()` 调用替换为 `model_config_of()`、`_resolved_attention_backends()` 替换为 `attention_backends_of()`。

# python/sglang/srt/arg_groups/cuda_graph_hook.py —— 迁移后的核心分支def disable_tc_piecewise_cudagraph_if_incompatible(server_args: Any):
    """TcPiecewise(torch.compile + piecewise)与下列配置不兼容。    大部分规则来自 torch.compile / dynamo 的限制;每条规则读 resolved 或 resolving
    view 上已经决议的值,不再触碰 record 方法。
    """
    # 函数级导入:避免模块顶层循环依赖,同时保持调用点接缝可 patch
    from sglang.srt.arg_groups.overrides import model_config_of
​
    cfg = resolving_view(server_args)
    rules = [
        # model-arch 黑名单:原 server_args.get_model_config().is_piecewise_...
        # 迁移后经自由函数读取同一个 memo 化的模型配置
        ("model-arch blacklist",
         lambda: model_config_of(server_args).is_piecewise_cuda_graph_disabled_model),
        ("DP attention", lambda: resolved_view(server_args).enable_dp_attention),
        ("full torch.compile mode", lambda: cfg.enable_torch_compile),
        ("pipeline parallelism (pp_size > 1)", lambda: cfg.pp_size > 1),
        # LoRA 在 tc_piecewise 下被 dynamo 阻断(per-batch LoRABatchInfo
        # rebinds 破坏 break guards);breakable/full 支持 LoRA
        ("LoRA", lambda: bool(cfg.lora_paths) or cfg.enable_lora),
        ("multimodal model",
         lambda: model_config_of(server_args).is_multimodal
         and not model_config_of(
             server_args
         ).is_multimodal_piecewise_cuda_graph_supported),
        ("CPU offload / hierarchical cache",
         lambda: cfg.cpu_offload_gb > 0 or cfg.enable_hierarchical_cache),
    ]
    ...

评论区精华

cutedsl_moe_max_num_tokens 遗漏调用方导致 AttributeError 正确性

Codex bot 指出 commit 5 删除 `server_args.cutedsl_moe_max_num_tokens()` 后,`qwen35_flashinfer_fusion.py:40` 的 `resolve_max_m()` 仍以方法形式调用,真实 ServerArgs 实例会在 CUDA graph 捕获前的 `prepare_qwen35_flashinfer_fusion()` 中抛 AttributeError。

结论:建议更新该调用方使用新自由函数;PR 已合并,需后续修复确认。 · 待处理

风险与影响

本 PR 是 50 文件、约 1000 行净改动的大规模重构,风险集中在:

  1. 遗漏调用方:Codex 已指出 qwen35_flashinfer_fusion.py:40cutedsl_moe_max_num_tokens() 调用未被迁移,真实 ServerArgs 会抛 AttributeError。这是 commit 5 引入的运行时回归源头,且发生在 Qwen3.5 MNNVL 特定路径上,常规 CI 未必覆盖。
  2. 测试接缝语义变化from ... import 是拷贝语义,原先 patch ServerArgs.use_mla_backend 类属性即可全局生效;现在改为按调用点导入后,所有测试必须改为 patch 模块绑定,遗漏会导致测试静默失效或误报。
  3. memo 生命周期model_config_of 的 memo 仍挂在 record 上,依赖 pickle 到子进程的语义;若未来出现不在 record 上创建 memo 的路径,可能重复解析或读到过期配置。
  4. 导入抖动server_args.py 删除了 globmathis_hip 等大量导入,若有其他模块仍经 server_args 间接使用这些符号会立刻断裂。
  • 对用户:无直接行为变化,但 Qwen3.5 MNNVL CuTeDSL 路径存在潜在 AttributeError 回归。
  • 对系统ServerArgs 减少 624 行、29 个方法,依赖方向收敛为 arg_groupsoverrides 单向;后续配置解析的 patch 接缝从类属性改为模块函数,测试维护方式发生范式转变。
  • 对团队:这是 5-PR 堆叠系列的第 2 环,后续 #36973-#36975 都依赖本 PR 的接口边界;合并后需要全量 CI 验证,特别是 Qwen3.5、MNNVL、CUDA graph 相关用例。
遗漏调用方回归 测试接缝语义变化 memo 生命周期依赖 跨 50 文件大规模重构 Qwen3.5 MNNVL 特定路径风险

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论