Skip to content

config: the derived parallel widths are computed from the leaves - #36790

Merged
ch-wan merged 2 commits into
mainfrom
cheng/gc-r3b-widths
Aug 28, 2026
Merged

config: the derived parallel widths are computed from the leaves#36790
ch-wan merged 2 commits into
mainfrom
cheng/gc-r3b-widths

Conversation

@ch-wan

@ch-wan ch-wan commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Motivation

get_parallel() answers a name one way: what the operator configures comes from the published
parallel bag, what only exists once the process groups are up comes from the groups. Six
names broke that rule — attn_tp_size, attn_dp_size, moe_ep_size, moe_tp_size,
dcp_enabled and attn_dcp_size. None is a parallel leaf: each is a
quotient of leaves that no flag sets, and each was read back off the group coordinator that had
just been built from those leaves.

That made them the one corner of get_parallel() where a name needs distributed init rather
than a publish, and the difference is invisible at the call site:

get_parallel().tp_size        # ValueError("config namespace 'parallel' not published")
get_parallel().attn_tp_size   # a group getter's bare AssertionError

Modifications

The arithmetic moves to runtime_context.derive_parallel_widths.
initialize_model_parallel builds its groups from that dict and stamps it, so the width a
group was built at and the width a reader gets cannot drift apart.
initialize_dp_attention stamps attn_dp_size; update_dp_attention_post_scale restamps it
where it already updates the live one; destroy_model_parallel clears the stamp.

That last case is why a plain per-read derivation is not enough. Elastic EP rewrites dp_size
and ep_size on the published bag while the coordinators keep their construction width, so
tp_size == attn_tp_size * attn_dp_size * attn_cp_size stops holding after a scale-up: a
formula evaluated on every read would answer with a width that shrank, or with zero.
world_size stays a live read and is deliberately not in the set. It is not a quotient of the
leaves, and its getter is right at every moment: a stamp taken when the groups are built would
answer with the launch count after try_admit_scale_ranks expands WORLD, and with the joining
cohort's own width on a scale-joiner, which lays its groups out at tp * pp while WORLD spans
ep_join_rank_offset + tp * pp.

A read is the override, then the stamp, then the live group. The fallback keeps a process that
installed groups without going through initialize_model_parallel working; when neither is
present, the error names which of the two is missing instead of surfacing a group getter's
assertion.

The second commit removes the last duplicate of the arithmetic.
compute_dp_attention_world_info recomputed attn_dp_size and attn_tp_size with the same
two lines. It stays — four of its six callers want only the ranks it returns, and ranks are
per-process, so they are not part of the stamped set — but the widths in it now come from
derive_attention_widths, which derive_parallel_widths calls as well.

Accuracy Tests

TestDerivedWidths is new. It pins the quotients, the stamp winning over the group, an
override winning over the stamp, the restamp, the named failure when neither is present, that
neither parallel_state.py nor dp_attention.py carries a second copy of a quotient, and that
the rank helper and the stamp agree across three topologies.

The 159 registered tests that mention the record report the same failure set as the base, and
the 24-shape resolution dump is field-for-field identical.

Speed Tests and Profiling

None. A stamped width is a dict lookup instead of an attribute read on a group coordinator.

Checklist

Review and Merge Process

Small enough to read in full, and worth reading in full: the elastic-EP case is the whole
argument for stamping rather than deriving per read.

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): ❌ Run #33194068745
Latest PR Test (Extra): ❌ Run #33194068628
Latest PR Test (AMD ROCm 7.2): 🚫 Run #33194068935

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6767a69319

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

moe_dp_size=moe_data_model_parallel_size,
dcp_size=decode_context_parallel_size,
dcp_enabled=_DCP is not None,
world_size=world_size,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve expanded WORLD size for scale joiners

When an elastic scale joiner starts with a nonzero ep_join_rank_offset, bootstrap.py initializes WORLD with rank_offset + tp_size * pp_size, but initialize_model_parallel() deliberately replaces its local world_size variable with tp_size * pp_size when recovered_rank=True. Stamping that local value makes get_parallel().world_size return only the joining cohort's size instead of the actual expanded WORLD size previously returned by get_world_size(); derive this value from the live world group or pass the original distributed size separately.

Useful? React with 👍 / 👎.

Comment thread python/sglang/srt/runtime_context.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 859562673c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@property
def attn_dp_size(self) -> int:
return self._v("attn_dp_size", _dp().get_attention_dp_size)
return self._derived_width("attn_dp_size", _dp().get_attention_dp_size)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor temporary DP-size overrides

When code enters dp_attention.disable_dp_size() for a draft-model scope, that context manager changes the live _ATTN_DP_SIZE to 1, but this property now returns the previously stamped value without consulting the live getter. Consequently, callers using the canonical get_parallel().attn_dp_size inside that scope observe the target model's DP width rather than the disabled width, unlike the pre-change behavior; temporarily override/restamp the derived value together with _ATTN_DP_SIZE.

Useful? React with 👍 / 👎.

@ch-wan
ch-wan force-pushed the cheng/gc-r3b-widths branch from 8595626 to 90c7cc1 Compare August 28, 2026 07:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 90c7cc125e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread python/sglang/srt/runtime_context.py
@ch-wan
ch-wan force-pushed the cheng/gc-r3b-widths branch from 90c7cc1 to ba959c3 Compare August 28, 2026 07:15
@ch-wan
ch-wan force-pushed the cheng/gc-r3a-disperse branch from 35d45cb to f4472ae Compare August 28, 2026 07:52
@ch-wan
ch-wan force-pushed the cheng/gc-r3b-widths branch from ba959c3 to dcb2776 Compare August 28, 2026 07:52
@ch-wan
ch-wan force-pushed the cheng/gc-r3a-disperse branch from f4472ae to 6be2ce6 Compare August 28, 2026 17:13
@ch-wan
ch-wan force-pushed the cheng/gc-r3b-widths branch from dcb2776 to 3e2d584 Compare August 28, 2026 17:13
Base automatically changed from cheng/gc-r3a-disperse to main August 28, 2026 17:17
ch-wan and others added 2 commits August 28, 2026 17:17
… read back

`get_parallel().attn_tp_size` and its five siblings -- `attn_dp_size`,
`moe_ep_size`, `moe_tp_size`, `dcp_enabled`, `attn_dcp_size` -- were read back
off the group coordinators that had just been built from the
configured leaves. That made them the one corner of `get_parallel()` where a
name needs distributed init rather than a publish, and the two spellings are
indistinguishable at the call site: `tp_size` raises `ValueError("'parallel'
not published")`, `attn_tp_size` raised a group getter's bare `AssertionError`.

The arithmetic moves to `runtime_context.derive_parallel_widths`, and
`initialize_model_parallel` builds its groups from that dict and stamps it. One
formula, one place: the width a group was built at and the width a reader gets
cannot drift apart. `initialize_dp_attention` stamps `attn_dp_size`, and
`update_dp_attention_post_scale` restamps it where it already updates the live
one -- which is the case that makes a plain per-read derivation wrong. Elastic
EP rewrites `dp_size` and `ep_size` on the published bag while the coordinators
keep their construction width, so `tp_size == attn_tp_size * attn_dp_size *
attn_cp_size` stops holding after a scale-up; deriving on every read would
answer with a width that shrank, or zero.

`world_size` stays a live read and is deliberately not in the set. It is not a
quotient of the leaves, and its getter is right at every moment: a stamp taken
when the groups are built would answer with the launch count after
`try_admit_scale_ranks` expands WORLD, and with the joining cohort's own width
on a scale-joiner, which lays its groups out at `tp * pp` while WORLD spans
`ep_join_rank_offset + tp * pp`.

A scope that moves a width temporarily moves the derived one with it, the way
`patch_tensor_parallel_group` already does for `tp_size`: `disable_dp_size()`
runs a draft worker without DP attention, and overrides `attn_dp_size` for its
duration so the legacy getter and `get_parallel()` cannot disagree inside it.

A read is the override, then the stamp, then the live group. The fallback keeps
a process that installed groups without `initialize_model_parallel` working;
when neither is present the error says which of the two is missing.

`TestDerivedWidths` pins the quotients, the stamp winning over the group, the
override winning over the stamp, the restamp, the named failure, and that
`parallel_state` no longer carries its own copy of the arithmetic.

The 265 registered tests that mention the record report the same failure set as
the base commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…k helper

`compute_dp_attention_world_info` recomputed `attn_dp_size` and `attn_tp_size`
with the same two lines the stamped derivation uses. It cannot go away -- four
of its six callers want only the ranks it returns, and ranks are per-process,
so they are not part of the stamped set -- but the widths in it were a second
copy that could drift from the one the groups are built at.

`derive_attention_widths` is now the single home for that pair;
`derive_parallel_widths` and the rank helper both call it. `initialize_dp_attention`
had a third copy -- it called the rank helper, dropped the width it returns and
recomputed `dp_size if enable_dp_attention else 1` three lines below -- and now
takes what the helper already gave it.

The guard that already refused a duplicate quotient in `parallel_state.py`
covers `dp_attention.py` too, and refuses that `else 1` spelling as well, which
the quotient patterns did not match. A new case checks the helper and the stamp
agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ch-wan
ch-wan force-pushed the cheng/gc-r3b-widths branch from 3e2d584 to 4b7ea9b Compare August 28, 2026 17:18
@ch-wan
ch-wan merged commit 43c63a2 into main Aug 28, 2026
22 of 32 checks passed
@ch-wan
ch-wan deleted the cheng/gc-r3b-widths branch August 28, 2026 17:18
saturn-acc pushed a commit to saturn-acc/sglang that referenced this pull request Aug 31, 2026
nzr-niu pushed a commit to nzr-niu/sglang that referenced this pull request Sep 1, 2026
thanhhao98 pushed a commit to thanhhao98/sglang that referenced this pull request Sep 9, 2026
attn_dcp_size is now a derived parallel width resolved at publish time
(sgl-project#36790, sgl-project#38113), so it no longer follows an overridden dcp_enabled and a
draft forward kept the target's DCP width. State the widths the guard
means, the way the other production override sites do.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant