Prhub

#36620 config: a parallel leaf with no live counterpart is read bare

原始 PR 作者 ch-wan 合并时间 2026-08-28 03:56 文件变更 125 提交数 1 评论 2 代码增减 +343 / -405

执行摘要

并行配置叶子直读,消除 config 跳转并重写 301 处调用点

PR body 指出:get_parallel() 服务的是 live process groups,而解析出的 parallel 配置在 .config 之后一跳。parallel 命名空间的 40 个叶子中 35 个没有 live counterpart(如 dp_sizeep_sizennodesenable_dp_attention 等纯配置项),对它们而言 .config 跳转没有任何消歧作用——every reader paid for a distinction that does not exist。因此本次让这类叶子直接裸读。

值得精读。重点看 runtime_context.py_parallel_config_leaves()__getattr__ 设计:叶子集合从字段元数据推导而非 bag、错误语义区分 ValueErrorAttributeError、把 Dynamo 可追踪性作为一等测试对象。对大型机械重写感兴趣的读者,PR body 中关于恒真断言的自述很有借鉴意义。

讨论亮点

本 PR 没有人工 review 评论。作者在 PR body 中主动披露了机械重写的一个真实失误:launch-path guard 测试里的断言被 AST 重写成了 bare == bare 恒真式,而这恰恰是用于钉住该行为的测试。其结论是:任何对'拼写被测对象'的测试做机械 sweep 时都存在同类隐患。Codex 自动审查给出 Didn't find any major issues. Breezy!,未提出修改建议。

实现拆解

  1. 使能 __getattr__ 裸读取python/sglang/srt/runtime_context.py):新增 _parallel_config_leaves(),用 functools.lru_cache 缓存,从 namespace_of(ServerArgs) 字段元数据推导 parallel 命名空间下所有叶子名(bag 未发布时不能从 bag 推导);改写 __getattr__:已发布且名字在配置字段中时返回 getattr(config, name);未发布但属于配置叶子时抛 ValueError("config namespace 'parallel' not published");未知名与下划线名仍抛 AttributeError(下划线分支同时打断 pickle/copy 协议在 __init__ 前的属性探测递归)。
  2. AST 驱动的调用点重写:118 个生产文件中的 301 处 get_parallel().config.<leaf> 改写为 get_parallel().<leaf>(296 处全量拼写 + 5 处经局部别名读取,别名绑定一并重写);接收者被解析为 get_parallel() 或绑定到它的局部变量,且只重写 config-only 叶子集合内的名字。5 个有 live 对应物的遮蔽 size 本次不动,留给后续 PR #36621 统一拼写。
  3. Dynamo 兼容性验证:gate helpers 会在编译 forward 内读取 parallel 叶子,object.__getattribute__ 会 graph-break;作者实测裸访问在 torch.compile(fullgraph=True) 下可追踪,并新增回归测试同时覆盖 .config 与裸拼写两种读法。
  4. 测试与正确性护栏:修复了机械重写把 launch-path guard 测试中断言改成 bare == bare 恒真式的问题(改为与 resolution_result 比较);24 种启动形状 × 478 个共享字段的解析 dump 对比 0 差异,唯一新增字段 grpc_worker_threads 从非字段槽位改为声明字段,两侧值均为 4;配置 guards 与涉及测试在每个提交边界全部通过。所有验证均为 CPU 侧,无 GPU 精度运行。
文件 模块 状态 重要度
python/sglang/srt/runtime_context.py 运行时上下文 modified 7.11
python/sglang/srt/managers/scheduler.py 调度器 modified 6.52
python/sglang/srt/model_executor/model_runner.py 模型运行器 modified 6.5
python/sglang/srt/managers/data_parallel_controller.py DP 控制器 modified 6.37
python/sglang/srt/managers/scheduler_pp_mixin.py PP 调度 modified 6.34
python/sglang/srt/disaggregation/common/conn.py 解耦连接 modified 6.25

关键符号

_parallel_config_leaves ParallelContext.__getattr__

关键源码片段

python/sglang/srt/runtime_context.py core-logic

本 PR 的使能变更所在:新增 `_parallel_config_leaves()` 推导配置叶子集合,改写 `__getattr__` 支持裸读取,并保留未发布时的 `ValueError` 语义;所有 301 处调用点重写都依赖这里的语义。

# runtime_context.py —— 并行配置读取的使能层# 从 ServerArgs 字段元数据推导 parallel 命名空间下的叶子集合。
# 用 lru_cache 保证只求值一次;bag 在发布前不存在,所以必须从元数据推导。
@functools.lru_cache(maxsize=1)
def _parallel_config_leaves() -> frozenset:
    # namespace_of(ServerArgs) 返回 { 字段名 : 命名空间路径 },
    # 这里只保留路径以 "parallel" 开头的叶子。
    from sglang.srt.arg_groups.arg_utils import namespace_of
    from sglang.srt.server_args import ServerArgs
​
    return frozenset(
        field
        for field, path in namespace_of(ServerArgs).items()
        if path.split(".")[0] == "parallel"
    )
​
​
# __getattr__ 只在没有 live @property 与 slot 命中时被调用,
# 因此它就是 " 裸读取配置叶子 " 的入口。
def __getattr__(self, name):
    # 下划线开头保持 AttributeError:既拦截拼写错误,
    # 也打断 pickle/copy 在 __init__ 之前探测属性造成的递归。
    if name.startswith("_"):
        raise AttributeError(name)
​
    config = self._config
    if config is not None:
        # bag 已发布:配置叶子直接透传其值。
        if name in config._fields:
            return getattr(config, name)
    elif name in _parallel_config_leaves():
        # bag 未发布:给出专门的 ValueError,而不是误导性的 AttributeError。
        raise ValueError("config namespace 'parallel' not published")
​
    # 未知名字仍按普通属性缺失处理。
    raise AttributeError(f"ParallelContext has no {name!r}")
python/sglang/srt/managers/scheduler.py core-logic

调度器核心路径上的典型调用点:`enable_dp_attention`、`dp_size`、`ep_size` 等配置叶子改为裸读,直接影响 DP attention 拓扑计算与 `ParallelState` 构造。

# scheduler.py —— 调度器初始化时读取并行拓扑配置。
# 重写前这些读取是 get_parallel().config.<leaf>,现在配置叶子直接裸读。attn_tp_rank, attn_tp_size, attn_dp_rank, attn_dp_size = (
    compute_dp_attention_world_info(
        get_parallel().enable_dp_attention, # config-only 叶子,裸读
        tp_rank,
        get_parallel().config.tp_size, # tp_size 是 live 字段,保持原拼写
        get_parallel().dp_size, # dp_size 是 config-only,裸读
        get_parallel().attn_cp_size, # attn_cp_size 是 config-only,裸读
    )
)self.ps = ParallelState(
    tp_rank=tp_rank,
    tp_size=get_parallel().config.tp_size, # live 字段仍走 @property + override
    pp_rank=pp_rank,
    pp_size=get_parallel().config.pp_size,
    dp_rank=dp_rank,
    dp_size=get_parallel().dp_size,
    attn_tp_rank=attn_tp_rank,
    attn_tp_size=attn_tp_size,
    attn_cp_rank=attn_cp_rank,
    attn_cp_size=get_parallel().attn_cp_size,
    attn_dcp_rank=tp_rank % get_parallel().dcp_size,
    attn_dcp_size=get_parallel().dcp_size,
    attn_dp_rank=attn_dp_rank,
    attn_dp_size=attn_dp_size,
    moe_ep_rank=moe_ep_rank,
    moe_ep_size=get_parallel().ep_size, # ep_size 是 config-only,裸读
    moe_dp_rank=moe_dp_rank,
    moe_dp_size=get_parallel().moe_dp_size, # moe_dp_size 仍有 live 对应物
    gpu_id=gpu_id,
)

评论区精华

Codex 自动审查结论 other

作者触发 @codex 后,Codex Review 给出 `Didn't find any major issues. Breezy!`,未提出具体修改建议。PR body 中作者对恒真断言问题的自我剖析是唯一实质讨论点。

结论:无需修改;Codex 未发现主要问题,恒真断言已在本次修复并纳入测试。 · 已解决

风险与影响

  1. __getattr__ 成为 301 处调用点唯一的新读取入口,bag 发布时序若有回归,会以 ValueError 在启动路径成规模浮现,排查面较大。
  2. 无 GPU 精度运行,所有验证都是 CPU 侧(resolution dump、guards、单测);真实多进程拓扑下的读取行为(如 data_parallel_controller.py 的 worker 拉起、conn.py 的跨节点注册)未被覆盖。
  3. 机械重写可能引入静默测试退化,本次恒真断言已演示该风险;后续 sweep 需要额外护栏。
  4. Dynamo 追踪依赖 __getattr__ 的朴素实现,虽已有回归测试钉住,但新调用点若出现在非常规编译路径仍可能 graph-break。

功能层面无任何可见变化:不改变解析结果、内核或调度形状,只改变配置读取位置,属于纯重构。系统层面影响 118 个生产文件,横跨调度器(scheduler.py)、模型运行器(model_runner.py)、DP 控制器(data_parallel_controller.py)、PP 循环(scheduler_pp_mixin.py)、解耦连接(conn.py)等核心模块,是后续 #36621(消除遮蔽 size 双拼写)与 #36622(记录不再作为对象传递)的地基。团队层面,配置读取契约从 get_parallel().config.<leaf> 收敛为 get_parallel().<leaf>,并把 torch.compile(fullgraph=True) 纳入配置路径回归。

跨模块机械重写(118 文件) 启动路径真实进程组未覆盖 无 GPU 精度验证 恒真断言风险(已修复)

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论